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; 296 char paddr[64], psize[64], pname[64], *mname; 297 dev_info_t *root; 298 uint64_t size, addr; 299 int ret; 300 301 bootfs_mkroot(bfs); 302 root = ddi_root_node(); 303 304 for (;;) { 305 if (id == UINT32_MAX) 306 break; 307 308 if (snprintf(paddr, sizeof (paddr), "module-addr-%d", id) > 309 sizeof (paddr)) 310 break; 311 312 if (snprintf(psize, sizeof (paddr), "module-size-%d", id) > 313 sizeof (paddr)) 314 break; 315 316 if (snprintf(pname, sizeof (paddr), "module-name-%d", id) > 317 sizeof (paddr)) 318 break; 319 320 addr = (uint64_t)ddi_prop_get_int64(DDI_DEV_T_ANY, root, 321 DDI_PROP_DONTPASS, paddr, 0); 322 if (addr == 0 || addr == DDI_PROP_NOT_FOUND) 323 break; 324 325 size = (uint64_t)ddi_prop_get_int64(DDI_DEV_T_ANY, root, 326 DDI_PROP_DONTPASS, psize, 0); 327 if (size == 0 || size == DDI_PROP_NOT_FOUND) 328 break; 329 330 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, root, 331 DDI_PROP_DONTPASS, pname, &mname) != DDI_PROP_SUCCESS) 332 break; 333 334 ret = bootfs_construct_entry(bfs, addr, size, mname); 335 if (ret == EINVAL) 336 bfs->bfs_stat.bfss_ndiscards.value.ui32++; 337 if (ret == EEXIST) 338 bfs->bfs_stat.bfss_ndups.value.ui32++; 339 ddi_prop_free(mname); 340 341 id++; 342 } 343 } 344 345 void 346 bootfs_destruct(bootfs_t *bfs) 347 { 348 bootfs_node_t *bnp; 349 350 while ((bnp = list_remove_head(&bfs->bfs_nodes)) != NULL) { 351 ASSERT(bnp->bvn_vnp->v_count == 1); 352 VN_RELE(bnp->bvn_vnp); 353 kmem_free(bnp->bvn_name, strlen(bnp->bvn_name) + 1); 354 kmem_cache_free(bootfs_node_cache, bnp); 355 } 356 } 357