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