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 (c) 2015 Joyent, Inc.  All rights reserved.
  14  */
  15 
  16 /*
  17  * This file takes care of reading the boot time modules and constructing them
  18  * into the appropriate series of vnodes.
  19  */
  20 
  21 #include <sys/conf.h>
  22 #include <sys/ddi.h>
  23 #include <sys/sunddi.h>
  24 #include <sys/vfs.h>
  25 #include <sys/sysmacros.h>
  26 #include <sys/stat.h>
  27 
  28 #include <sys/fs/bootfs_impl.h>
  29 
  30 kmem_cache_t *bootfs_node_cache;
  31 
  32 static const vattr_t bootfs_vattr_dir = {
  33         AT_ALL,                                 /* va_mask */
  34         VDIR,                                   /* va_type */
  35         S_IFDIR | 0555,                         /* va_mode */
  36         0,                                      /* va_uid */
  37         0,                                      /* va_gid */
  38         0,                                      /* va_fsid */
  39         0,                                      /* va_nodeid */
  40         1,                                      /* va_nlink */
  41         0,                                      /* va_size */
  42         0,                                      /* va_atime */
  43         0,                                      /* va_mtime */
  44         0,                                      /* va_ctime */
  45         0,                                      /* va_rdev */
  46         0,                                      /* va_blksize */
  47         0,                                      /* va_nblocks */
  48         0                                       /* va_seq */
  49 };
  50 
  51 static const vattr_t bootfs_vattr_reg = {
  52         AT_ALL,                                 /* va_mask */
  53         VREG,                                   /* va_type */
  54         S_IFREG | 0555,                         /* va_mode */
  55         0,                                      /* va_uid */
  56         0,                                      /* va_gid */
  57         0,                                      /* va_fsid */
  58         0,                                      /* va_nodeid */
  59         1,                                      /* va_nlink */
  60         0,                                      /* va_size */
  61         0,                                      /* va_atime */
  62         0,                                      /* va_mtime */
  63         0,                                      /* va_ctime */
  64         0,                                      /* va_rdev */
  65         0,                                      /* va_blksize */
  66         0,                                      /* va_nblocks */
  67         0                                       /* va_seq */
  68 };
  69 
  70 /*ARGSUSED*/
  71 int
  72 bootfs_node_constructor(void *buf, void *arg, int kmflags)
  73 {
  74         bootfs_node_t *bnp = buf;
  75 
  76         bnp->bvn_vnp = vn_alloc(kmflags);
  77         if (bnp->bvn_vnp == NULL)
  78                 return (-1);
  79 
  80         return (0);
  81 }
  82 
  83 /*ARGSUSED*/
  84 void
  85 bootfs_node_destructor(void *buf, void *arg)
  86 {
  87         bootfs_node_t *bnp = buf;
  88 
  89         vn_free(bnp->bvn_vnp);
  90 }
  91 
  92 static int
  93 bootfs_comparator(const void *a, const void *b)
  94 {
  95         const bootfs_node_t *lfs, *rfs;
  96         int ret;
  97 
  98         lfs = a;
  99         rfs = b;
 100 
 101         ret = strcmp(lfs->bvn_name, rfs->bvn_name);
 102         if (ret > 0)
 103                 ret = 1;
 104         if (ret < 0)
 105                 ret = -1;
 106         return (ret);
 107 }
 108 
 109 static void
 110 bootfs_node_init(bootfs_t *bfs, bootfs_node_t *bnp, const struct vattr *vap,
 111     const char *name, size_t namelen)
 112 {
 113         timestruc_t now;
 114 
 115         vn_reinit(bnp->bvn_vnp);
 116 
 117         bnp->bvn_vnp->v_flag |= VNOSWAP;
 118         bnp->bvn_vnp->v_type = vap->va_type;
 119         bnp->bvn_vnp->v_vfsp = bfs->bfs_vfsp;
 120         bnp->bvn_vnp->v_rdev = 0;
 121         bnp->bvn_vnp->v_data = (caddr_t)bnp;
 122         vn_setops(bnp->bvn_vnp, bootfs_vnodeops);
 123 
 124         bnp->bvn_name = kmem_alloc(namelen + 1, KM_SLEEP);
 125         bcopy(name, bnp->bvn_name, namelen);
 126         bnp->bvn_name[namelen] = '\0';
 127         if (vap->va_type == VDIR) {
 128                 avl_create(&bnp->bvn_dir, bootfs_comparator,
 129                     sizeof (bootfs_node_t),
 130                     offsetof(bootfs_node_t, bvn_link));
 131         }
 132         bzero(&bnp->bvn_link, sizeof (avl_node_t));
 133         bcopy(vap, &bnp->bvn_attr, sizeof (vattr_t));
 134 
 135         gethrestime(&now);
 136         bnp->bvn_attr.va_atime = now;
 137         bnp->bvn_attr.va_ctime = now;
 138         bnp->bvn_attr.va_mtime = now;
 139         bnp->bvn_attr.va_fsid = makedevice(bootfs_major, bfs->bfs_minor);
 140         bnp->bvn_attr.va_nodeid = bfs->bfs_ninode;
 141         bnp->bvn_attr.va_blksize = PAGESIZE;
 142         bfs->bfs_ninode++;
 143         list_insert_tail(&bfs->bfs_nodes, bnp);
 144 }
 145 
 146 static void
 147 bootfs_mkroot(bootfs_t *bfs)
 148 {
 149         bootfs_node_t *bnp;
 150 
 151         bnp = kmem_cache_alloc(bootfs_node_cache, KM_SLEEP);
 152         bootfs_node_init(bfs, bnp, &bootfs_vattr_dir, "/", 1);
 153         bnp->bvn_vnp->v_flag |= VROOT;
 154         bnp->bvn_parent = bnp;
 155         bfs->bfs_rootvn = bnp;
 156         bfs->bfs_stat.bfss_ndirs.value.ui32++;
 157         vn_exists(bnp->bvn_vnp);
 158 }
 159 
 160 static int
 161 bootfs_mknode(bootfs_t *bfs, bootfs_node_t *parent, bootfs_node_t **outp,
 162     const char *name, size_t namelen, const vattr_t *vap, uintptr_t addr,
 163     uint64_t size)
 164 {
 165         bootfs_node_t *bnp;
 166         bootfs_node_t sn;
 167         avl_index_t where;
 168         char *buf;
 169 
 170         ASSERT(parent->bvn_attr.va_type == VDIR);
 171         buf = kmem_alloc(namelen + 1, KM_SLEEP);
 172         bcopy(name, buf, namelen);
 173         buf[namelen] = '\0';
 174         sn.bvn_name = buf;
 175         if ((bnp = avl_find(&parent->bvn_dir, &sn, &where)) != NULL) {
 176                 kmem_free(buf, namelen + 1);
 177                 /* Directories can collide, files cannot */
 178                 if (vap->va_type == VDIR) {
 179                         *outp = bnp;
 180                         return (0);
 181                 }
 182                 return (EEXIST);
 183         }
 184         kmem_free(buf, namelen + 1);
 185 
 186         bnp = kmem_cache_alloc(bootfs_node_cache, KM_SLEEP);
 187         bootfs_node_init(bfs, bnp, vap, name, namelen);
 188         bnp->bvn_parent = parent;
 189         avl_add(&parent->bvn_dir, bnp);
 190         *outp = bnp;
 191 
 192         if (vap->va_type == VDIR) {
 193                 parent->bvn_attr.va_size++;
 194                 parent->bvn_attr.va_nlink++;
 195                 bfs->bfs_stat.bfss_ndirs.value.ui32++;
 196         } else {
 197                 bnp->bvn_addr = addr;
 198                 bnp->bvn_size = size;
 199                 bfs->bfs_stat.bfss_nfiles.value.ui32++;
 200                 bfs->bfs_stat.bfss_nbytes.value.ui64 += size;
 201                 bnp->bvn_attr.va_nblocks = P2ROUNDUP(size, 512) >> 9;
 202                 bnp->bvn_attr.va_size = size;
 203         }
 204 
 205         vn_exists(bnp->bvn_vnp);
 206 
 207         return (0);
 208 }
 209 
 210 /*
 211  * Given the address, size, and path a boot-time module would like, go through
 212  * and create all of the directory entries that are required and then the file
 213  * itself. If someone has passed in a module that has the same name as another
 214  * one, we honor the first one.
 215  */
 216 static int
 217 bootfs_construct_entry(bootfs_t *bfs, uintptr_t addr, uint64_t size,
 218     const char *mname)
 219 {
 220         char *sp;
 221         size_t nlen;
 222         int ret;
 223         bootfs_node_t *nbnp;
 224 
 225         const char *p = mname;
 226         bootfs_node_t *bnp = bfs->bfs_rootvn;
 227 
 228         if (*p == '\0')
 229                 return (EINVAL);
 230 
 231         for (;;) {
 232                 /* First eliminate all leading / characters. */
 233                 while (*p == '/')
 234                         p++;
 235 
 236                 /* A name with all slashes or ending in a / */
 237                 if (*p == '\0')
 238                         return (EINVAL);
 239 
 240                 sp = strchr(p, '/');
 241                 if (sp == NULL)
 242                         break;
 243                 nlen = (ptrdiff_t)sp - (ptrdiff_t)p;
 244                 if (strncmp(p, ".", nlen) == 0) {
 245                         p = sp + 1;
 246                         continue;
 247                 }
 248 
 249                 if (strncmp(p, "..", nlen) == 0) {
 250                         bnp = bnp->bvn_parent;
 251                         p = sp + 1;
 252                         continue;
 253                 }
 254 
 255                 VERIFY(bootfs_mknode(bfs, bnp, &nbnp, p, nlen,
 256                     &bootfs_vattr_dir, addr, size) == 0);
 257                 p = sp + 1;
 258                 bnp = nbnp;
 259         }
 260 
 261         nlen = strlen(p);
 262         ret = bootfs_mknode(bfs, bnp, &nbnp, p, nlen, &bootfs_vattr_reg,
 263             addr, size);
 264         if (ret != 0)
 265                 return (ret);
 266 
 267         return (0);
 268 }
 269 
 270 /*
 271  * We're going to go through every boot time module and construct the
 272  * appropriate vnodes for them now. Because there are very few of these that
 273  * exist, generally on the order of a handful, we're going to create them all
 274  * when the file system is initialized and then tear them all down when the
 275  * module gets unloaded.
 276  *
 277  * The information about the modules is contained in properties on the root of
 278  * the devinfo tree. Specifically there are three properties per module:
 279  *
 280  *   - module-size-%d   int64_t size, in bytes, of the boot time module.
 281  *   - module-addr-%d   The address of the boot time module
 282  *   - module-name-%d   The string name of the boot time module
 283  *
 284  * Note that the module-size and module-addr fields are always 64-bit values
 285  * regardless of being on a 32-bit or 64-bit kernel. module-name is a string
 286  * property.
 287  *
 288  * There is no property that indicates the total number of such modules. Modules
 289  * start at 0 and work their way up incrementally. The first time we can't find
 290  * a module or a property, then we stop.
 291  */
 292 void
 293 bootfs_construct(bootfs_t *bfs)
 294 {
 295         uint_t id = 0, ndata;
 296         char paddr[64], psize[64], pname[64], *mname;
 297         dev_info_t *root;
 298         uchar_t *datap;
 299         uint64_t size = 0, addr = 0;
 300         int ret;
 301 
 302         bootfs_mkroot(bfs);
 303         root = ddi_root_node();
 304 
 305         for (;;) {
 306                 if (id == UINT32_MAX)
 307                         break;
 308 
 309                 if (snprintf(paddr, sizeof (paddr), "module-addr-%d", id) >
 310                     sizeof (paddr))
 311                         break;
 312 
 313                 if (snprintf(psize, sizeof (paddr), "module-size-%d", id) >
 314                     sizeof (paddr))
 315                         break;
 316 
 317                 if (snprintf(pname, sizeof (paddr), "module-name-%d", id) >
 318                     sizeof (paddr))
 319                         break;
 320 
 321                 if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, root,
 322                     DDI_PROP_DONTPASS, paddr, &datap, &ndata) !=
 323                     DDI_PROP_SUCCESS)
 324                         break;
 325 
 326                 if (ndata == 8)
 327                         bcopy(datap, &addr, sizeof (uint64_t));
 328                 ddi_prop_free(datap);
 329                 if (ndata != 8)
 330                         break;
 331 
 332                 if (ddi_prop_lookup_byte_array(DDI_DEV_T_ANY, root,
 333                     DDI_PROP_DONTPASS, psize, &datap, &ndata) !=
 334                     DDI_PROP_SUCCESS)
 335                         break;
 336                 if (ndata == 8)
 337                         bcopy(datap, &size, sizeof (uint64_t));
 338                 ddi_prop_free(datap);
 339                 if (ndata != 8)
 340                         break;
 341 
 342                 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, root,
 343                     DDI_PROP_DONTPASS, pname, &mname) != DDI_PROP_SUCCESS)
 344                         break;
 345 
 346                 ret = bootfs_construct_entry(bfs, addr, size, mname);
 347                 if (ret == EINVAL)
 348                         bfs->bfs_stat.bfss_ndiscards.value.ui32++;
 349                 if (ret == EEXIST)
 350                         bfs->bfs_stat.bfss_ndups.value.ui32++;
 351                 ddi_prop_free(mname);
 352 
 353                 id++;
 354         }
 355 }
 356 
 357 void
 358 bootfs_destruct(bootfs_t *bfs)
 359 {
 360         bootfs_node_t *bnp;
 361 
 362         while ((bnp = list_remove_head(&bfs->bfs_nodes)) != NULL) {
 363                 ASSERT(bnp->bvn_vnp->v_count == 1);
 364                 VN_RELE(bnp->bvn_vnp);
 365                 kmem_free(bnp->bvn_name, strlen(bnp->bvn_name) + 1);
 366                 kmem_cache_free(bootfs_node_cache, bnp);
 367         }
 368 }