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