devices.c revision 43d537e0deeb4ba20e13db22e4fc28b87a4621c7
1/* 2 * Copyright (C) 2007-2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <errno.h> 18#include <fnmatch.h> 19#include <stddef.h> 20#include <stdio.h> 21#include <stdlib.h> 22#include <sys/stat.h> 23#include <sys/types.h> 24 25#include <fcntl.h> 26#include <dirent.h> 27#include <unistd.h> 28#include <string.h> 29 30#include <sys/socket.h> 31#include <sys/un.h> 32#include <linux/netlink.h> 33 34#include <selinux/selinux.h> 35#include <selinux/label.h> 36#include <selinux/android.h> 37#include <selinux/avc.h> 38 39#include <private/android_filesystem_config.h> 40#include <sys/time.h> 41#include <sys/wait.h> 42 43#include <cutils/list.h> 44#include <cutils/uevent.h> 45 46#include "devices.h" 47#include "ueventd_parser.h" 48#include "util.h" 49#include "log.h" 50 51#define UNUSED __attribute__((__unused__)) 52 53#define SYSFS_PREFIX "/sys" 54#define FIRMWARE_DIR1 "/etc/firmware" 55#define FIRMWARE_DIR2 "/vendor/firmware" 56#define FIRMWARE_DIR3 "/firmware/image" 57 58extern struct selabel_handle *sehandle; 59 60static int device_fd = -1; 61 62struct uevent { 63 const char *action; 64 const char *path; 65 const char *subsystem; 66 const char *firmware; 67 const char *partition_name; 68 const char *device_name; 69 int partition_num; 70 int major; 71 int minor; 72}; 73 74struct perms_ { 75 char *name; 76 char *attr; 77 mode_t perm; 78 unsigned int uid; 79 unsigned int gid; 80 unsigned short prefix; 81 unsigned short wildcard; 82}; 83 84struct perm_node { 85 struct perms_ dp; 86 struct listnode plist; 87}; 88 89struct platform_node { 90 char *name; 91 char *path; 92 int path_len; 93 struct listnode list; 94}; 95 96static list_declare(sys_perms); 97static list_declare(dev_perms); 98static list_declare(platform_names); 99 100int add_dev_perms(const char *name, const char *attr, 101 mode_t perm, unsigned int uid, unsigned int gid, 102 unsigned short prefix, 103 unsigned short wildcard) { 104 struct perm_node *node = calloc(1, sizeof(*node)); 105 if (!node) 106 return -ENOMEM; 107 108 node->dp.name = strdup(name); 109 if (!node->dp.name) 110 return -ENOMEM; 111 112 if (attr) { 113 node->dp.attr = strdup(attr); 114 if (!node->dp.attr) 115 return -ENOMEM; 116 } 117 118 node->dp.perm = perm; 119 node->dp.uid = uid; 120 node->dp.gid = gid; 121 node->dp.prefix = prefix; 122 node->dp.wildcard = wildcard; 123 124 if (attr) 125 list_add_tail(&sys_perms, &node->plist); 126 else 127 list_add_tail(&dev_perms, &node->plist); 128 129 return 0; 130} 131 132void fixup_sys_perms(const char *upath) 133{ 134 char buf[512]; 135 struct listnode *node; 136 struct perms_ *dp; 137 char *secontext; 138 139 /* upaths omit the "/sys" that paths in this list 140 * contain, so we add 4 when comparing... 141 */ 142 list_for_each(node, &sys_perms) { 143 dp = &(node_to_item(node, struct perm_node, plist))->dp; 144 if (dp->prefix) { 145 if (strncmp(upath, dp->name + 4, strlen(dp->name + 4))) 146 continue; 147 } else if (dp->wildcard) { 148 if (fnmatch(dp->name + 4, upath, FNM_PATHNAME) != 0) 149 continue; 150 } else { 151 if (strcmp(upath, dp->name + 4)) 152 continue; 153 } 154 155 if ((strlen(upath) + strlen(dp->attr) + 6) > sizeof(buf)) 156 return; 157 158 sprintf(buf,"/sys%s/%s", upath, dp->attr); 159 INFO("fixup %s %d %d 0%o\n", buf, dp->uid, dp->gid, dp->perm); 160 chown(buf, dp->uid, dp->gid); 161 chmod(buf, dp->perm); 162 if (sehandle) { 163 secontext = NULL; 164 selabel_lookup(sehandle, &secontext, buf, 0); 165 if (secontext) { 166 setfilecon(buf, secontext); 167 freecon(secontext); 168 } 169 } 170 } 171} 172 173static bool perm_path_matches(const char *path, struct perms_ *dp) 174{ 175 if (dp->prefix) { 176 if (strncmp(path, dp->name, strlen(dp->name)) == 0) 177 return true; 178 } else if (dp->wildcard) { 179 if (fnmatch(dp->name, path, FNM_PATHNAME) == 0) 180 return true; 181 } else { 182 if (strcmp(path, dp->name) == 0) 183 return true; 184 } 185 186 return false; 187} 188 189static mode_t get_device_perm(const char *path, const char **links, 190 unsigned *uid, unsigned *gid) 191{ 192 mode_t perm; 193 struct listnode *node; 194 struct perm_node *perm_node; 195 struct perms_ *dp; 196 197 /* search the perms list in reverse so that ueventd.$hardware can 198 * override ueventd.rc 199 */ 200 list_for_each_reverse(node, &dev_perms) { 201 bool match = false; 202 203 perm_node = node_to_item(node, struct perm_node, plist); 204 dp = &perm_node->dp; 205 206 if (perm_path_matches(path, dp)) { 207 match = true; 208 } else { 209 if (links) { 210 int i; 211 for (i = 0; links[i]; i++) { 212 if (perm_path_matches(links[i], dp)) { 213 match = true; 214 break; 215 } 216 } 217 } 218 } 219 220 if (match) { 221 *uid = dp->uid; 222 *gid = dp->gid; 223 return dp->perm; 224 } 225 } 226 /* Default if nothing found. */ 227 *uid = 0; 228 *gid = 0; 229 return 0600; 230} 231 232static void make_device(const char *path, 233 const char *upath UNUSED, 234 int block, int major, int minor, 235 const char **links) 236{ 237 unsigned uid; 238 unsigned gid; 239 mode_t mode; 240 dev_t dev; 241 char *secontext = NULL; 242 243 mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR); 244 245 if (sehandle) { 246 selabel_lookup_best_match(sehandle, &secontext, path, links, mode); 247 setfscreatecon(secontext); 248 } 249 250 dev = makedev(major, minor); 251 /* Temporarily change egid to avoid race condition setting the gid of the 252 * device node. Unforunately changing the euid would prevent creation of 253 * some device nodes, so the uid has to be set with chown() and is still 254 * racy. Fixing the gid race at least fixed the issue with system_server 255 * opening dynamic input devices under the AID_INPUT gid. */ 256 setegid(gid); 257 mknod(path, mode, dev); 258 chown(path, uid, -1); 259 setegid(AID_ROOT); 260 261 if (secontext) { 262 freecon(secontext); 263 setfscreatecon(NULL); 264 } 265} 266 267static void add_platform_device(const char *path) 268{ 269 int path_len = strlen(path); 270 struct listnode *node; 271 struct platform_node *bus; 272 const char *name = path; 273 274 if (!strncmp(path, "/devices/", 9)) { 275 name += 9; 276 if (!strncmp(name, "platform/", 9)) 277 name += 9; 278 } 279 280 list_for_each_reverse(node, &platform_names) { 281 bus = node_to_item(node, struct platform_node, list); 282 if ((bus->path_len < path_len) && 283 (path[bus->path_len] == '/') && 284 !strncmp(path, bus->path, bus->path_len)) 285 /* subdevice of an existing platform, ignore it */ 286 return; 287 } 288 289 INFO("adding platform device %s (%s)\n", name, path); 290 291 bus = calloc(1, sizeof(struct platform_node)); 292 bus->path = strdup(path); 293 bus->path_len = path_len; 294 bus->name = bus->path + (name - path); 295 list_add_tail(&platform_names, &bus->list); 296} 297 298/* 299 * given a path that may start with a platform device, find the length of the 300 * platform device prefix. If it doesn't start with a platform device, return 301 * 0. 302 */ 303static struct platform_node *find_platform_device(const char *path) 304{ 305 int path_len = strlen(path); 306 struct listnode *node; 307 struct platform_node *bus; 308 309 list_for_each_reverse(node, &platform_names) { 310 bus = node_to_item(node, struct platform_node, list); 311 if ((bus->path_len < path_len) && 312 (path[bus->path_len] == '/') && 313 !strncmp(path, bus->path, bus->path_len)) 314 return bus; 315 } 316 317 return NULL; 318} 319 320static void remove_platform_device(const char *path) 321{ 322 struct listnode *node; 323 struct platform_node *bus; 324 325 list_for_each_reverse(node, &platform_names) { 326 bus = node_to_item(node, struct platform_node, list); 327 if (!strcmp(path, bus->path)) { 328 INFO("removing platform device %s\n", bus->name); 329 free(bus->path); 330 list_remove(node); 331 free(bus); 332 return; 333 } 334 } 335} 336 337/* Given a path that may start with a PCI device, populate the supplied buffer 338 * with the PCI domain/bus number and the peripheral ID and return 0. 339 * If it doesn't start with a PCI device, or there is some error, return -1 */ 340static int find_pci_device_prefix(const char *path, char *buf, ssize_t buf_sz) 341{ 342 const char *start, *end; 343 344 if (strncmp(path, "/devices/pci", 12)) 345 return -1; 346 347 /* Beginning of the prefix is the initial "pci" after "/devices/" */ 348 start = path + 9; 349 350 /* End of the prefix is two path '/' later, capturing the domain/bus number 351 * and the peripheral ID. Example: pci0000:00/0000:00:1f.2 */ 352 end = strchr(start, '/'); 353 if (!end) 354 return -1; 355 end = strchr(end + 1, '/'); 356 if (!end) 357 return -1; 358 359 /* Make sure we have enough room for the string plus null terminator */ 360 if (end - start + 1 > buf_sz) 361 return -1; 362 363 strncpy(buf, start, end - start); 364 buf[end - start] = '\0'; 365 return 0; 366} 367 368#if LOG_UEVENTS 369 370static inline suseconds_t get_usecs(void) 371{ 372 struct timeval tv; 373 gettimeofday(&tv, 0); 374 return tv.tv_sec * (suseconds_t) 1000000 + tv.tv_usec; 375} 376 377#define log_event_print(x...) INFO(x) 378 379#else 380 381#define log_event_print(fmt, args...) do { } while (0) 382#define get_usecs() 0 383 384#endif 385 386static void parse_event(const char *msg, struct uevent *uevent) 387{ 388 uevent->action = ""; 389 uevent->path = ""; 390 uevent->subsystem = ""; 391 uevent->firmware = ""; 392 uevent->major = -1; 393 uevent->minor = -1; 394 uevent->partition_name = NULL; 395 uevent->partition_num = -1; 396 uevent->device_name = NULL; 397 398 /* currently ignoring SEQNUM */ 399 while(*msg) { 400 if(!strncmp(msg, "ACTION=", 7)) { 401 msg += 7; 402 uevent->action = msg; 403 } else if(!strncmp(msg, "DEVPATH=", 8)) { 404 msg += 8; 405 uevent->path = msg; 406 } else if(!strncmp(msg, "SUBSYSTEM=", 10)) { 407 msg += 10; 408 uevent->subsystem = msg; 409 } else if(!strncmp(msg, "FIRMWARE=", 9)) { 410 msg += 9; 411 uevent->firmware = msg; 412 } else if(!strncmp(msg, "MAJOR=", 6)) { 413 msg += 6; 414 uevent->major = atoi(msg); 415 } else if(!strncmp(msg, "MINOR=", 6)) { 416 msg += 6; 417 uevent->minor = atoi(msg); 418 } else if(!strncmp(msg, "PARTN=", 6)) { 419 msg += 6; 420 uevent->partition_num = atoi(msg); 421 } else if(!strncmp(msg, "PARTNAME=", 9)) { 422 msg += 9; 423 uevent->partition_name = msg; 424 } else if(!strncmp(msg, "DEVNAME=", 8)) { 425 msg += 8; 426 uevent->device_name = msg; 427 } 428 429 /* advance to after the next \0 */ 430 while(*msg++) 431 ; 432 } 433 434 log_event_print("event { '%s', '%s', '%s', '%s', %d, %d }\n", 435 uevent->action, uevent->path, uevent->subsystem, 436 uevent->firmware, uevent->major, uevent->minor); 437} 438 439static char **get_character_device_symlinks(struct uevent *uevent) 440{ 441 const char *parent; 442 char *slash; 443 char **links; 444 int link_num = 0; 445 int width; 446 struct platform_node *pdev; 447 448 pdev = find_platform_device(uevent->path); 449 if (!pdev) 450 return NULL; 451 452 links = malloc(sizeof(char *) * 2); 453 if (!links) 454 return NULL; 455 memset(links, 0, sizeof(char *) * 2); 456 457 /* skip "/devices/platform/<driver>" */ 458 parent = strchr(uevent->path + pdev->path_len, '/'); 459 if (!*parent) 460 goto err; 461 462 if (!strncmp(parent, "/usb", 4)) { 463 /* skip root hub name and device. use device interface */ 464 while (*++parent && *parent != '/'); 465 if (*parent) 466 while (*++parent && *parent != '/'); 467 if (!*parent) 468 goto err; 469 slash = strchr(++parent, '/'); 470 if (!slash) 471 goto err; 472 width = slash - parent; 473 if (width <= 0) 474 goto err; 475 476 if (asprintf(&links[link_num], "/dev/usb/%s%.*s", uevent->subsystem, width, parent) > 0) 477 link_num++; 478 else 479 links[link_num] = NULL; 480 mkdir("/dev/usb", 0755); 481 } 482 else { 483 goto err; 484 } 485 486 return links; 487err: 488 free(links); 489 return NULL; 490} 491 492static char **get_block_device_symlinks(struct uevent *uevent) 493{ 494 const char *device; 495 struct platform_node *pdev; 496 char *slash; 497 const char *type; 498 int width; 499 char buf[256]; 500 char link_path[256]; 501 int fd; 502 int link_num = 0; 503 int ret; 504 char *p; 505 unsigned int size; 506 struct stat info; 507 508 pdev = find_platform_device(uevent->path); 509 if (pdev) { 510 device = pdev->name; 511 type = "platform"; 512 } else if (!find_pci_device_prefix(uevent->path, buf, sizeof(buf))) { 513 device = buf; 514 type = "pci"; 515 } else { 516 return NULL; 517 } 518 519 char **links = malloc(sizeof(char *) * 4); 520 if (!links) 521 return NULL; 522 memset(links, 0, sizeof(char *) * 4); 523 524 INFO("found %s device %s\n", type, device); 525 526 snprintf(link_path, sizeof(link_path), "/dev/block/%s/%s", type, device); 527 528 if (uevent->partition_name) { 529 p = strdup(uevent->partition_name); 530 sanitize(p); 531 if (strcmp(uevent->partition_name, p)) 532 NOTICE("Linking partition '%s' as '%s'\n", uevent->partition_name, p); 533 if (asprintf(&links[link_num], "%s/by-name/%s", link_path, p) > 0) 534 link_num++; 535 else 536 links[link_num] = NULL; 537 free(p); 538 } 539 540 if (uevent->partition_num >= 0) { 541 if (asprintf(&links[link_num], "%s/by-num/p%d", link_path, uevent->partition_num) > 0) 542 link_num++; 543 else 544 links[link_num] = NULL; 545 } 546 547 slash = strrchr(uevent->path, '/'); 548 if (asprintf(&links[link_num], "%s/%s", link_path, slash + 1) > 0) 549 link_num++; 550 else 551 links[link_num] = NULL; 552 553 return links; 554} 555 556static void handle_device(const char *action, const char *devpath, 557 const char *path, int block, int major, int minor, char **links) 558{ 559 int i; 560 561 if(!strcmp(action, "add")) { 562 make_device(devpath, path, block, major, minor, (const char **)links); 563 if (links) { 564 for (i = 0; links[i]; i++) 565 make_link(devpath, links[i]); 566 } 567 } 568 569 if(!strcmp(action, "remove")) { 570 if (links) { 571 for (i = 0; links[i]; i++) 572 remove_link(devpath, links[i]); 573 } 574 unlink(devpath); 575 } 576 577 if (links) { 578 for (i = 0; links[i]; i++) 579 free(links[i]); 580 free(links); 581 } 582} 583 584static void handle_platform_device_event(struct uevent *uevent) 585{ 586 const char *path = uevent->path; 587 588 if (!strcmp(uevent->action, "add")) 589 add_platform_device(path); 590 else if (!strcmp(uevent->action, "remove")) 591 remove_platform_device(path); 592} 593 594static const char *parse_device_name(struct uevent *uevent, unsigned int len) 595{ 596 const char *name; 597 598 /* if it's not a /dev device, nothing else to do */ 599 if((uevent->major < 0) || (uevent->minor < 0)) 600 return NULL; 601 602 /* do we have a name? */ 603 name = strrchr(uevent->path, '/'); 604 if(!name) 605 return NULL; 606 name++; 607 608 /* too-long names would overrun our buffer */ 609 if(strlen(name) > len) { 610 ERROR("DEVPATH=%s exceeds %u-character limit on filename; ignoring event\n", 611 name, len); 612 return NULL; 613 } 614 615 return name; 616} 617 618static void handle_block_device_event(struct uevent *uevent) 619{ 620 const char *base = "/dev/block/"; 621 const char *name; 622 char devpath[96]; 623 char **links = NULL; 624 625 name = parse_device_name(uevent, 64); 626 if (!name) 627 return; 628 629 snprintf(devpath, sizeof(devpath), "%s%s", base, name); 630 make_dir(base, 0755); 631 632 if (!strncmp(uevent->path, "/devices/", 9)) 633 links = get_block_device_symlinks(uevent); 634 635 handle_device(uevent->action, devpath, uevent->path, 1, 636 uevent->major, uevent->minor, links); 637} 638 639#define DEVPATH_LEN 96 640 641static bool assemble_devpath(char *devpath, const char *dirname, 642 const char *devname) 643{ 644 int s = snprintf(devpath, DEVPATH_LEN, "%s/%s", dirname, devname); 645 if (s < 0) { 646 ERROR("failed to assemble device path (%s); ignoring event\n", 647 strerror(errno)); 648 return false; 649 } else if (s >= DEVPATH_LEN) { 650 ERROR("%s/%s exceeds %u-character limit on path; ignoring event\n", 651 dirname, devname, DEVPATH_LEN); 652 return false; 653 } 654 return true; 655} 656 657static void mkdir_recursive_for_devpath(const char *devpath) 658{ 659 char dir[DEVPATH_LEN]; 660 char *slash; 661 662 strcpy(dir, devpath); 663 slash = strrchr(dir, '/'); 664 *slash = '\0'; 665 mkdir_recursive(dir, 0755); 666} 667 668static inline void __attribute__((__deprecated__)) kernel_logger() 669{ 670 INFO("kernel logger is deprecated\n"); 671} 672 673static void handle_generic_device_event(struct uevent *uevent) 674{ 675 char *base; 676 const char *name; 677 char devpath[DEVPATH_LEN] = {0}; 678 char **links = NULL; 679 680 name = parse_device_name(uevent, 64); 681 if (!name) 682 return; 683 684 struct ueventd_subsystem *subsystem = 685 ueventd_subsystem_find_by_name(uevent->subsystem); 686 687 if (subsystem) { 688 const char *devname; 689 690 switch (subsystem->devname_src) { 691 case DEVNAME_UEVENT_DEVNAME: 692 devname = uevent->device_name; 693 break; 694 695 case DEVNAME_UEVENT_DEVPATH: 696 devname = name; 697 break; 698 699 default: 700 ERROR("%s subsystem's devpath option is not set; ignoring event\n", 701 uevent->subsystem); 702 return; 703 } 704 705 if (!assemble_devpath(devpath, subsystem->dirname, devname)) 706 return; 707 mkdir_recursive_for_devpath(devpath); 708 } else if (!strncmp(uevent->subsystem, "usb", 3)) { 709 if (!strcmp(uevent->subsystem, "usb")) { 710 if (uevent->device_name) { 711 if (!assemble_devpath(devpath, "/dev", uevent->device_name)) 712 return; 713 mkdir_recursive_for_devpath(devpath); 714 } 715 else { 716 /* This imitates the file system that would be created 717 * if we were using devfs instead. 718 * Minors are broken up into groups of 128, starting at "001" 719 */ 720 int bus_id = uevent->minor / 128 + 1; 721 int device_id = uevent->minor % 128 + 1; 722 /* build directories */ 723 make_dir("/dev/bus", 0755); 724 make_dir("/dev/bus/usb", 0755); 725 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d", bus_id); 726 make_dir(devpath, 0755); 727 snprintf(devpath, sizeof(devpath), "/dev/bus/usb/%03d/%03d", bus_id, device_id); 728 } 729 } else { 730 /* ignore other USB events */ 731 return; 732 } 733 } else if (!strncmp(uevent->subsystem, "graphics", 8)) { 734 base = "/dev/graphics/"; 735 make_dir(base, 0755); 736 } else if (!strncmp(uevent->subsystem, "drm", 3)) { 737 base = "/dev/dri/"; 738 make_dir(base, 0755); 739 } else if (!strncmp(uevent->subsystem, "oncrpc", 6)) { 740 base = "/dev/oncrpc/"; 741 make_dir(base, 0755); 742 } else if (!strncmp(uevent->subsystem, "adsp", 4)) { 743 base = "/dev/adsp/"; 744 make_dir(base, 0755); 745 } else if (!strncmp(uevent->subsystem, "msm_camera", 10)) { 746 base = "/dev/msm_camera/"; 747 make_dir(base, 0755); 748 } else if(!strncmp(uevent->subsystem, "input", 5)) { 749 base = "/dev/input/"; 750 make_dir(base, 0755); 751 } else if(!strncmp(uevent->subsystem, "mtd", 3)) { 752 base = "/dev/mtd/"; 753 make_dir(base, 0755); 754 } else if(!strncmp(uevent->subsystem, "sound", 5)) { 755 base = "/dev/snd/"; 756 make_dir(base, 0755); 757 } else if(!strncmp(uevent->subsystem, "misc", 4) && 758 !strncmp(name, "log_", 4)) { 759 kernel_logger(); 760 base = "/dev/log/"; 761 make_dir(base, 0755); 762 name += 4; 763 } else 764 base = "/dev/"; 765 links = get_character_device_symlinks(uevent); 766 767 if (!devpath[0]) 768 snprintf(devpath, sizeof(devpath), "%s%s", base, name); 769 770 handle_device(uevent->action, devpath, uevent->path, 0, 771 uevent->major, uevent->minor, links); 772} 773 774static void handle_device_event(struct uevent *uevent) 775{ 776 if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change") || !strcmp(uevent->action, "online")) 777 fixup_sys_perms(uevent->path); 778 779 if (!strncmp(uevent->subsystem, "block", 5)) { 780 handle_block_device_event(uevent); 781 } else if (!strncmp(uevent->subsystem, "platform", 8)) { 782 handle_platform_device_event(uevent); 783 } else { 784 handle_generic_device_event(uevent); 785 } 786} 787 788static int load_firmware(int fw_fd, int loading_fd, int data_fd) 789{ 790 struct stat st; 791 long len_to_copy; 792 int ret = 0; 793 794 if(fstat(fw_fd, &st) < 0) 795 return -1; 796 len_to_copy = st.st_size; 797 798 write(loading_fd, "1", 1); /* start transfer */ 799 800 while (len_to_copy > 0) { 801 char buf[PAGE_SIZE]; 802 ssize_t nr; 803 804 nr = read(fw_fd, buf, sizeof(buf)); 805 if(!nr) 806 break; 807 if(nr < 0) { 808 ret = -1; 809 break; 810 } 811 812 len_to_copy -= nr; 813 while (nr > 0) { 814 ssize_t nw = 0; 815 816 nw = write(data_fd, buf + nw, nr); 817 if(nw <= 0) { 818 ret = -1; 819 goto out; 820 } 821 nr -= nw; 822 } 823 } 824 825out: 826 if(!ret) 827 write(loading_fd, "0", 1); /* successful end of transfer */ 828 else 829 write(loading_fd, "-1", 2); /* abort transfer */ 830 831 return ret; 832} 833 834static int is_booting(void) 835{ 836 return access("/dev/.booting", F_OK) == 0; 837} 838 839static void process_firmware_event(struct uevent *uevent) 840{ 841 char *root, *loading, *data, *file1 = NULL, *file2 = NULL, *file3 = NULL; 842 int l, loading_fd, data_fd, fw_fd; 843 int booting = is_booting(); 844 845 INFO("firmware: loading '%s' for '%s'\n", 846 uevent->firmware, uevent->path); 847 848 l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path); 849 if (l == -1) 850 return; 851 852 l = asprintf(&loading, "%sloading", root); 853 if (l == -1) 854 goto root_free_out; 855 856 l = asprintf(&data, "%sdata", root); 857 if (l == -1) 858 goto loading_free_out; 859 860 l = asprintf(&file1, FIRMWARE_DIR1"/%s", uevent->firmware); 861 if (l == -1) 862 goto data_free_out; 863 864 l = asprintf(&file2, FIRMWARE_DIR2"/%s", uevent->firmware); 865 if (l == -1) 866 goto data_free_out; 867 868 l = asprintf(&file3, FIRMWARE_DIR3"/%s", uevent->firmware); 869 if (l == -1) 870 goto data_free_out; 871 872 loading_fd = open(loading, O_WRONLY); 873 if(loading_fd < 0) 874 goto file_free_out; 875 876 data_fd = open(data, O_WRONLY); 877 if(data_fd < 0) 878 goto loading_close_out; 879 880try_loading_again: 881 fw_fd = open(file1, O_RDONLY); 882 if(fw_fd < 0) { 883 fw_fd = open(file2, O_RDONLY); 884 if (fw_fd < 0) { 885 fw_fd = open(file3, O_RDONLY); 886 if (fw_fd < 0) { 887 if (booting) { 888 /* If we're not fully booted, we may be missing 889 * filesystems needed for firmware, wait and retry. 890 */ 891 usleep(100000); 892 booting = is_booting(); 893 goto try_loading_again; 894 } 895 INFO("firmware: could not open '%s' %d\n", uevent->firmware, errno); 896 write(loading_fd, "-1", 2); 897 goto data_close_out; 898 } 899 } 900 } 901 902 if(!load_firmware(fw_fd, loading_fd, data_fd)) 903 INFO("firmware: copy success { '%s', '%s' }\n", root, uevent->firmware); 904 else 905 INFO("firmware: copy failure { '%s', '%s' }\n", root, uevent->firmware); 906 907 close(fw_fd); 908data_close_out: 909 close(data_fd); 910loading_close_out: 911 close(loading_fd); 912file_free_out: 913 free(file1); 914 free(file2); 915 free(file3); 916data_free_out: 917 free(data); 918loading_free_out: 919 free(loading); 920root_free_out: 921 free(root); 922} 923 924static void handle_firmware_event(struct uevent *uevent) 925{ 926 pid_t pid; 927 int ret; 928 929 if(strcmp(uevent->subsystem, "firmware")) 930 return; 931 932 if(strcmp(uevent->action, "add")) 933 return; 934 935 /* we fork, to avoid making large memory allocations in init proper */ 936 pid = fork(); 937 if (!pid) { 938 process_firmware_event(uevent); 939 exit(EXIT_SUCCESS); 940 } 941} 942 943#define UEVENT_MSG_LEN 1024 944void handle_device_fd() 945{ 946 char msg[UEVENT_MSG_LEN+2]; 947 int n; 948 while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) { 949 if(n >= UEVENT_MSG_LEN) /* overflow -- discard */ 950 continue; 951 952 msg[n] = '\0'; 953 msg[n+1] = '\0'; 954 955 struct uevent uevent; 956 parse_event(msg, &uevent); 957 958 if (sehandle && selinux_status_updated() > 0) { 959 struct selabel_handle *sehandle2; 960 sehandle2 = selinux_android_file_context_handle(); 961 if (sehandle2) { 962 selabel_close(sehandle); 963 sehandle = sehandle2; 964 } 965 } 966 967 handle_device_event(&uevent); 968 handle_firmware_event(&uevent); 969 } 970} 971 972/* Coldboot walks parts of the /sys tree and pokes the uevent files 973** to cause the kernel to regenerate device add events that happened 974** before init's device manager was started 975** 976** We drain any pending events from the netlink socket every time 977** we poke another uevent file to make sure we don't overrun the 978** socket's buffer. 979*/ 980 981static void do_coldboot(DIR *d) 982{ 983 struct dirent *de; 984 int dfd, fd; 985 986 dfd = dirfd(d); 987 988 fd = openat(dfd, "uevent", O_WRONLY); 989 if(fd >= 0) { 990 write(fd, "add\n", 4); 991 close(fd); 992 handle_device_fd(); 993 } 994 995 while((de = readdir(d))) { 996 DIR *d2; 997 998 if(de->d_type != DT_DIR || de->d_name[0] == '.') 999 continue; 1000 1001 fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY); 1002 if(fd < 0) 1003 continue; 1004 1005 d2 = fdopendir(fd); 1006 if(d2 == 0) 1007 close(fd); 1008 else { 1009 do_coldboot(d2); 1010 closedir(d2); 1011 } 1012 } 1013} 1014 1015static void coldboot(const char *path) 1016{ 1017 DIR *d = opendir(path); 1018 if(d) { 1019 do_coldboot(d); 1020 closedir(d); 1021 } 1022} 1023 1024void device_init(void) 1025{ 1026 suseconds_t t0, t1; 1027 struct stat info; 1028 int fd; 1029 1030 sehandle = NULL; 1031 if (is_selinux_enabled() > 0) { 1032 sehandle = selinux_android_file_context_handle(); 1033 selinux_status_open(true); 1034 } 1035 1036 /* is 256K enough? udev uses 16MB! */ 1037 device_fd = uevent_open_socket(256*1024, true); 1038 if(device_fd < 0) 1039 return; 1040 1041 fcntl(device_fd, F_SETFD, FD_CLOEXEC); 1042 fcntl(device_fd, F_SETFL, O_NONBLOCK); 1043 1044 if (stat(coldboot_done, &info) < 0) { 1045 t0 = get_usecs(); 1046 coldboot("/sys/class"); 1047 coldboot("/sys/block"); 1048 coldboot("/sys/devices"); 1049 t1 = get_usecs(); 1050 fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000); 1051 close(fd); 1052 log_event_print("coldboot %ld uS\n", ((long) (t1 - t0))); 1053 } else { 1054 log_event_print("skipping coldboot, already done\n"); 1055 } 1056} 1057 1058int get_device_fd() 1059{ 1060 return device_fd; 1061} 1062