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 <driver-info/driver-info.h>
6
7#include <elf.h>
8#include <fcntl.h>
9#include <stdint.h>
10#include <stdio.h>
11#include <string.h>
12#include <unistd.h>
13
14#include <zircon/driver/binding.h>
15#include <zircon/types.h>
16
17typedef zx_status_t (*note_func_t)(void* note, size_t sz, void* cookie);
18
19typedef Elf64_Ehdr elfhdr;
20typedef Elf64_Phdr elfphdr;
21typedef Elf64_Nhdr notehdr;
22
23static zx_status_t find_note(const char* name, size_t nlen, uint32_t type,
24                             void* data, size_t size,
25                             note_func_t func, void* cookie) {
26    while (size >= sizeof(notehdr)) {
27        const notehdr* hdr = data;
28        uint32_t nsz = (hdr->n_namesz + 3) & (~3);
29        if (nsz > (size - sizeof(notehdr))) {
30            return ZX_ERR_INTERNAL;
31        }
32        size_t hsz = sizeof(notehdr) + nsz;
33        data += hsz;
34        size -= hsz;
35
36        uint32_t dsz = (hdr->n_descsz + 3) & (~3);
37        if (dsz > size) {
38            return ZX_ERR_INTERNAL;
39        }
40
41        if ((hdr->n_type == type) &&
42            (hdr->n_namesz == nlen) &&
43            (memcmp(name, hdr + 1, nlen) == 0)) {
44            return func(data - hsz, hdr->n_descsz + hsz, cookie);
45        }
46
47        data += dsz;
48        size -= dsz;
49    }
50    return ZX_ERR_NOT_FOUND;
51}
52
53static zx_status_t for_each_note(void* obj, di_read_func_t diread,
54                                 const char* name, uint32_t type,
55                                 void* data, size_t dsize,
56                                 note_func_t func, void* cookie) {
57    size_t nlen = strlen(name) + 1;
58    elfphdr ph[64];
59    elfhdr eh;
60    if (diread(obj, &eh, sizeof(eh), 0) != ZX_OK) {
61        printf("for_each_note: pread(eh) failed\n");
62        return ZX_ERR_IO;
63    }
64    if (memcmp(&eh, ELFMAG, 4) ||
65        (eh.e_ehsize != sizeof(elfhdr)) ||
66        (eh.e_phentsize != sizeof(elfphdr))) {
67        printf("for_each_note: bad elf magic\n");
68        return ZX_ERR_INTERNAL;
69    }
70    size_t sz = sizeof(elfphdr) * eh.e_phnum;
71    if (sz > sizeof(ph)) {
72        printf("for_each_note: too many phdrs\n");
73        return ZX_ERR_INTERNAL;
74    }
75    if (diread(obj, ph, sz, eh.e_phoff) != ZX_OK) {
76        printf("for_each_note: pread(sz,eh.e_phoff) failed\n");
77        return ZX_ERR_IO;
78    }
79    for (int i = 0; i < eh.e_phnum; i++) {
80        if ((ph[i].p_type != PT_NOTE) ||
81            (ph[i].p_filesz > dsize)) {
82            continue;
83        }
84        if (diread(obj, data, ph[i].p_filesz, ph[i].p_offset) != ZX_OK) {
85            printf("for_each_note: pread(ph[i]) failed\n");
86            return ZX_ERR_IO;
87        }
88        int r = find_note(name, nlen, type, data, ph[i].p_filesz, func, cookie);
89        if (r == ZX_OK) {
90            return r;
91        }
92    }
93    return ZX_ERR_NOT_FOUND;
94}
95
96typedef struct {
97    void* cookie;
98    di_info_func_t func;
99} context;
100
101static zx_status_t callback(void* note, size_t sz, void* _ctx) {
102    context* ctx = _ctx;
103    zircon_driver_note_t* dn = note;
104    if (sz < sizeof(zircon_driver_note_t)) {
105        return ZX_ERR_INTERNAL;
106    }
107    size_t max = (sz - sizeof(zircon_driver_note_t)) / sizeof(zx_bind_inst_t);
108    if (dn->payload.bindcount > max) {
109        return ZX_ERR_INTERNAL;
110    }
111    const zx_bind_inst_t* binding = (const void*)(&dn->payload + 1);
112    ctx->func(&dn->payload, binding, ctx->cookie);
113    return ZX_OK;
114}
115
116static zx_status_t di_pread(void* obj, void* data, size_t len, size_t off) {
117    if (pread(*((int*)obj), data, len, off) == (ssize_t)len) {
118        return ZX_OK;
119    } else {
120        return ZX_ERR_IO;
121    }
122}
123
124zx_status_t di_read_driver_info_etc(void* obj, di_read_func_t rfunc,
125                                    void *cookie, di_info_func_t func) {
126    context ctx = {
127        .cookie = cookie,
128        .func = func,
129    };
130    uint8_t data[4096];
131    return for_each_note(obj, rfunc,
132                         ZIRCON_NOTE_NAME, ZIRCON_NOTE_DRIVER,
133                         data, sizeof(data), callback, &ctx);
134}
135
136zx_status_t di_read_driver_info(int fd, void *cookie, di_info_func_t func) {
137    context ctx = {
138        .cookie = cookie,
139        .func = func,
140    };
141    uint8_t data[4096];
142    return for_each_note(&fd, di_pread,
143                         ZIRCON_NOTE_NAME, ZIRCON_NOTE_DRIVER,
144                         data, sizeof(data), callback, &ctx);
145}
146
147const char* di_bind_param_name(uint32_t param_num) {
148    switch (param_num) {
149    case BIND_FLAGS:                  return "Flags";
150    case BIND_PROTOCOL:               return "Protocol";
151    case BIND_AUTOBIND:               return "Autobind";
152    case BIND_PCI_VID:                return "PCI.VID";
153    case BIND_PCI_DID:                return "PCI.DID";
154    case BIND_PCI_CLASS:              return "PCI.Class";
155    case BIND_PCI_SUBCLASS:           return "PCI.Subclass";
156    case BIND_PCI_INTERFACE:          return "PCI.Interface";
157    case BIND_PCI_REVISION:           return "PCI.Revision";
158    case BIND_PCI_BDF_ADDR:           return "PCI.BDFAddr";
159    case BIND_USB_VID:                return "USB.VID";
160    case BIND_USB_PID:                return "USB.PID";
161    case BIND_USB_CLASS:              return "USB.Class";
162    case BIND_USB_SUBCLASS:           return "USB.Subclass";
163    case BIND_USB_PROTOCOL:           return "USB.Protocol";
164    case BIND_PLATFORM_DEV_VID:       return "PlatDev.VID";
165    case BIND_PLATFORM_DEV_PID:       return "PlatDev.PID";
166    case BIND_PLATFORM_DEV_DID:       return "PlatDev.DID";
167    case BIND_ACPI_HID_0_3:           return "ACPI.HID[0-3]";
168    case BIND_ACPI_HID_4_7:           return "ACPI.HID[4-7]";
169    case BIND_IHDA_CODEC_VID:         return "IHDA.Codec.VID";
170    case BIND_IHDA_CODEC_DID:         return "IHDA.Codec.DID";
171    case BIND_IHDA_CODEC_MAJOR_REV:   return "IHDACodec.MajorRev";
172    case BIND_IHDA_CODEC_MINOR_REV:   return "IHDACodec.MinorRev";
173    case BIND_IHDA_CODEC_VENDOR_REV:  return "IHDACodec.VendorRev";
174    case BIND_IHDA_CODEC_VENDOR_STEP: return "IHDACodec.VendorStep";
175    default: return NULL;
176    }
177}
178
179void di_dump_bind_inst(const zx_bind_inst_t* b, char* buf, size_t buf_len) {
180    if (!b || !buf || !buf_len) {
181        return;
182    }
183
184    uint32_t cc = BINDINST_CC(b->op);
185    uint32_t op = BINDINST_OP(b->op);
186    uint32_t pa = BINDINST_PA(b->op);
187    uint32_t pb = BINDINST_PB(b->op);
188    size_t off = 0;
189    buf[0] = 0;
190
191    switch (op) {
192    case OP_ABORT:
193    case OP_MATCH:
194    case OP_GOTO:
195    case OP_SET:
196    case OP_CLEAR:
197        break;
198    case OP_LABEL:
199        snprintf(buf + off, buf_len - off, "L.%u:", pa);
200        return;
201    default:
202        snprintf(buf + off, buf_len - off,
203                "Unknown Op 0x%1x [0x%08x, 0x%08x]", op, b->op, b->arg);
204        return;
205    }
206
207    off += snprintf(buf + off, buf_len - off, "if (");
208    if (cc == COND_AL) {
209        off += snprintf(buf + off, buf_len - off, "true");
210    } else {
211        const char* pb_name = di_bind_param_name(pb);
212        if (pb_name) {
213            off += snprintf(buf + off, buf_len - off, "%s", pb_name);
214        } else {
215            off += snprintf(buf + off, buf_len - off, "P.%04x", pb);
216        }
217
218        switch (cc) {
219        case COND_EQ:
220            off += snprintf(buf + off, buf_len - off, " == 0x%08x", b->arg);
221            break;
222        case COND_NE:
223            off += snprintf(buf + off, buf_len - off, " != 0x%08x", b->arg);
224            break;
225        case COND_GT:
226            off += snprintf(buf + off, buf_len - off, " > 0x%08x", b->arg);
227            break;
228        case COND_LT:
229            off += snprintf(buf + off, buf_len - off, " < 0x%08x", b->arg);
230            break;
231        case COND_GE:
232            off += snprintf(buf + off, buf_len - off, " >= 0x%08x", b->arg);
233            break;
234        case COND_LE:
235            off += snprintf(buf + off, buf_len - off, " <= 0x%08x", b->arg);
236            break;
237        case COND_MASK:
238            off += snprintf(buf + off, buf_len - off, " & 0x%08x != 0", b->arg);
239            break;
240        case COND_BITS:
241            off += snprintf(buf + off, buf_len - off, " & 0x%08x == 0x%08x", b->arg, b->arg);
242            break;
243        default:
244            off += snprintf(buf + off, buf_len - off,
245                            " ?(0x%x) 0x%08x", cc, b->arg);
246            break;
247        }
248    }
249    off += snprintf(buf + off, buf_len - off, ") ");
250
251    switch (op) {
252    case OP_ABORT:
253        off += snprintf(buf + off, buf_len - off, "return no-match;");
254        break;
255    case OP_MATCH:
256        off += snprintf(buf + off, buf_len - off, "return match;");
257        break;
258    case OP_GOTO:
259        off += snprintf(buf + off, buf_len - off, "goto L.%u;", b->arg);
260        break;
261    case OP_SET:
262        off += snprintf(buf + off, buf_len - off, "flags |= 0x%02x;", pa);
263        break;
264    case OP_CLEAR:
265        off += snprintf(buf + off, buf_len - off, "flags &= 0x%02x;", ~pa & 0xFF);
266        break;
267    }
268}
269