1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <dirent.h> 6#include <fcntl.h> 7#include <stdio.h> 8#include <stdlib.h> 9#include <string.h> 10#include <unistd.h> 11 12#include "devmgr.h" 13#include "devcoordinator.h" 14#include "log.h" 15 16#include <driver-info/driver-info.h> 17 18#include <zircon/driver/binding.h> 19#include <zxcpp/new.h> 20 21typedef struct { 22 const char* libname; 23 void (*func)(driver_t* drv, const char* version); 24} add_ctx_t; 25 26static bool is_driver_disabled(const char* name) { 27 // driver.<driver_name>.disable 28 char opt[16 + DRIVER_NAME_LEN_MAX]; 29 snprintf(opt, 16 + DRIVER_NAME_LEN_MAX, "driver.%s.disable", name); 30 return getenv_bool(opt, false); 31} 32 33static void found_driver(zircon_driver_note_payload_t* note, 34 const zx_bind_inst_t* bi, void* cookie) { 35 auto ctx = static_cast<add_ctx_t*>(cookie); 36 37 // ensure strings are terminated 38 note->name[sizeof(note->name) - 1] = 0; 39 note->vendor[sizeof(note->vendor) - 1] = 0; 40 note->version[sizeof(note->version) - 1] = 0; 41 42 if (is_driver_disabled(note->name)) { 43 return; 44 } 45 46 const char* libname = ctx->libname; 47 size_t pathlen = strlen(libname) + 1; 48 size_t namelen = strlen(note->name) + 1; 49 size_t bindlen = note->bindcount * sizeof(zx_bind_inst_t); 50 size_t len = sizeof(driver_t) + bindlen + pathlen + namelen; 51 52 if ((note->flags & ZIRCON_DRIVER_NOTE_FLAG_ASAN) && !dc_asan_drivers) { 53 if (dc_launched_first_devhost) { 54 log(ERROR, "%s (%s) requires ASan: cannot load after boot;" 55 " consider devmgr.devhost.asan=true\n", 56 libname, note->name); 57 return; 58 } 59 dc_asan_drivers = true; 60 } 61 62 driver_t* drv; 63 if ((drv = static_cast<driver_t*>(malloc(len))) == nullptr) { 64 return; 65 } 66 new (drv) driver_t; 67 68 memset(drv, 0, sizeof(driver_t)); 69 drv->binding_size = static_cast<uint32_t>(bindlen); 70 drv->binding = reinterpret_cast<const zx_bind_inst_t*>(drv + 1); 71 drv->libname = reinterpret_cast<const char*>(drv->binding + note->bindcount); 72 drv->name = drv->libname + pathlen; 73 74 memcpy((void*) drv->binding, bi, bindlen); 75 memcpy((void*) drv->libname, libname, pathlen); 76 memcpy((void*) drv->name, note->name, namelen); 77 78#if VERBOSE_DRIVER_LOAD 79 printf("found driver: %s\n", (char*) cookie); 80 printf(" name: %s\n", note->name); 81 printf(" vendor: %s\n", note->vendor); 82 printf(" version: %s\n", note->version); 83 printf(" flags: %#x\n", note->flags); 84 printf(" binding:\n"); 85 for (size_t n = 0; n < note->bindcount; n++) { 86 printf(" %03zd: %08x %08x\n", n, bi[n].op, bi[n].arg); 87 } 88#endif 89 90 ctx->func(drv, note->version); 91} 92 93void find_loadable_drivers(const char* path, 94 void (*func)(driver_t* drv, const char* version)) { 95 96 DIR* dir = opendir(path); 97 if (dir == nullptr) { 98 return; 99 } 100 struct dirent* de; 101 while ((de = readdir(dir)) != nullptr) { 102 if (de->d_name[0] == '.') { 103 continue; 104 } 105 if (de->d_type != DT_REG) { 106 continue; 107 } 108 char libname[256 + 32]; 109 int r = snprintf(libname, sizeof(libname), "%s/%s", path, de->d_name); 110 if ((r < 0) || (r >= (int)sizeof(libname))) { 111 continue; 112 } 113 114 int fd; 115 if ((fd = openat(dirfd(dir), de->d_name, O_RDONLY)) < 0) { 116 continue; 117 } 118 add_ctx_t ctx = { 119 .libname = libname, 120 .func = func, 121 }; 122 zx_status_t status = di_read_driver_info(fd, &ctx, found_driver); 123 close(fd); 124 125 if (status) { 126 if (status == ZX_ERR_NOT_FOUND) { 127 printf("devcoord: no driver info in '%s'\n", libname); 128 } else { 129 printf("devcoord: error reading info from '%s'\n", libname); 130 } 131 } 132 } 133 closedir(dir); 134} 135 136void load_driver(const char* path, 137 void (*func)(driver_t* drv, const char* version)) { 138 //TODO: check for duplicate driver add 139 int fd; 140 if ((fd = open(path, O_RDONLY)) < 0) { 141 printf("devcoord: cannot open '%s'\n", path); 142 return; 143 } 144 145 add_ctx_t ctx = { 146 .libname = path, 147 .func = func, 148 }; 149 zx_status_t status = di_read_driver_info(fd, &ctx, found_driver); 150 close(fd); 151 152 if (status) { 153 if (status == ZX_ERR_NOT_FOUND) { 154 printf("devcoord: no driver info in '%s'\n", path); 155 } else { 156 printf("devcoord: error reading info from '%s'\n", path); 157 } 158 } 159} 160