-
Notifications
You must be signed in to change notification settings - Fork 2
/
binding.h
286 lines (238 loc) · 10.7 KB
/
binding.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <assert.h>
#include <zircon/compiler.h>
#include <stdalign.h>
#include <stddef.h>
#include <stdint.h>
__BEGIN_CDECLS;
// COAABBBB VVVVVVVV Condition Opcode paramA paramB Value
#define OP_ABORT 0x0 // if (cond) return no-match
#define OP_MATCH 0x1 // if (cond) return match
#define OP_GOTO 0x2 // if (cond) advance to next LABEL(paramA)
#define OP_SET 0x3 // if (cond) flags |= paramA
#define OP_CLEAR 0x4 // if (cond) flags &= (~paramA)
#define OP_LABEL 0x5 // no-op, labels line with paramA
#define COND_AL 0x0 // true
#define COND_EQ 0x1 // bind(paramB) == Value
#define COND_NE 0x2 // bind(paramB) != Value
#define COND_GT 0x3 // bind(paramB) > Value
#define COND_LT 0x4 // bind(paramB) < Value
#define COND_GE 0x5 // bind(paramB) >= Value
#define COND_LE 0x6 // bind(paramB) <= Value
#define COND_MASK 0x7 // (bind(paramB) & Value) != 0
#define COND_BITS 0x8 // (bind(paramB) & Value) == Value
// branches are forward-only
// branches always go to the first matching LABEL
// branches that cannot find a matching LABEL are treated as ABORTs
// there is an implied unconditional ABORT after the last instruction
// flags are initially zero, may be set/cleared with SET/CLEAR
// flags may be tested by comparison against BIND_FLAGS
#define BINDINST(c,o,a,b,v) \
{ (((c)&0xF)<<28)|(((o)&0xF)<<24)|(((a)&0xFF)<<16)|((b)&0xFFFF),(v) }
#define BINDINST_CC(n) ((n) >> 28)
#define BINDINST_OP(n) (((n) >> 24) & 0xF)
#define BINDINST_PA(n) (((n) >> 16) & 0xFF)
#define BINDINST_PB(n) ((n) & 0xFFFF)
#define BI_ABORT() BINDINST(COND_AL,OP_ABORT,0,0,0)
#define BI_MATCH() BINDINST(COND_AL,OP_MATCH,0,0,0)
#define BI_GOTO(n) BINDINST(COND_AL,OP_GOTO,n,0,0)
#define BI_SET(f) BINDINST(COND_AL,OP_SET,f,0,0)
#define BI_CLEAR(f) BINDINST(COND_AL,OP_CLEAR,f,0,0)
#define BI_LABEL(n) BINDINST(COND_AL,OP_LABEL,n,0,0)
#define BI_ABORT_IF(c,b,v) BINDINST(COND_##c,OP_ABORT,0,b,v)
#define BI_MATCH_IF(c,b,v) BINDINST(COND_##c,OP_MATCH,0,b,v)
#define BI_GOTO_IF(c,b,v,n) BINDINST(COND_##c,OP_GOTO,n,b,v)
#define BI_SET_IF(c,b,v,f) BINDINST(COND_##c,OP_SET,f,b,v)
#define BI_CLEAR_IF(c,b,v,f) BINDINST(COND_##c,OP_CLEAR,f,b,v)
// for drivers that only want to be bound on user request
#define BI_ABORT_IF_AUTOBIND BI_ABORT_IF(NE, BIND_AUTOBIND, 0)
// global binding variables at 0x00XX
#define BIND_FLAGS 0x0000 // value of the flags register
#define BIND_PROTOCOL 0x0001 // primary protcol of the device
#define BIND_AUTOBIND 0x0002 // if this is an automated bind/load
// pci binding variables at 0x01XX
#define BIND_PCI_VID 0x0100
#define BIND_PCI_DID 0x0101
#define BIND_PCI_CLASS 0x0102
#define BIND_PCI_SUBCLASS 0x0103
#define BIND_PCI_INTERFACE 0x0104
#define BIND_PCI_REVISION 0x0105
#define BIND_PCI_BDF_ADDR 0x0106
// pci binding variable utils
#define BIND_PCI_BDF_PACK(bus, dev, func) \
((((uint32_t)(bus) & 0xFF) << 8) | \
(((uint32_t)(dev) & 0x1F) << 3) | \
((uint32_t)(func) & 0x07))
#define BIND_PCI_BDF_UNPACK_BUS(bdf) (((uint32_t)(bdf) >> 8) & 0xFF)
#define BIND_PCI_BDF_UNPACK_DEV(bdf) (((uint32_t)(bdf) >> 3) & 0x1F)
#define BIND_PCI_BDF_UNPACK_FUNC(bdf) ((uint32_t)(bdf) & 0x07)
// usb binding variables at 0x02XX
// these are used for both ZX_PROTOCOL_USB and ZX_PROTOCOL_USB_FUNCTION
#define BIND_USB_VID 0x0200
#define BIND_USB_PID 0x0201
#define BIND_USB_CLASS 0x0202
#define BIND_USB_SUBCLASS 0x0203
#define BIND_USB_PROTOCOL 0x0204
// Platform bus binding variables at 0x03XX
#define BIND_PLATFORM_DEV_VID 0x0300
#define BIND_PLATFORM_DEV_PID 0x0301
#define BIND_PLATFORM_DEV_DID 0x0302
#define BIND_PLATFORM_PROTO 0x0303
// ACPI binding variables at 0x04XX
// The _HID is a 7- or 8-byte string. Because a bind property is 32-bit, use 2
// properties to bind using the _HID. They are encoded in big endian order for
// human readability. In the case of 7-byte _HID's, the 8th-byte shall be 0.
#define BIND_ACPI_HID_0_3 0x0400 // char 0-3
#define BIND_ACPI_HID_4_7 0x0401 // char 4-7
// The _CID may be a valid HID value or a bus-specific string. The ACPI bus
// driver only publishes those that are valid HID values.
#define BIND_ACPI_CID_0_3 0x0402 // char 0-3
#define BIND_ACPI_CID_4_7 0x0403 // char 4-7
// Intel HDA Codec binding variables at 0x05XX
#define BIND_IHDA_CODEC_VID 0x0500
#define BIND_IHDA_CODEC_DID 0x0501
#define BIND_IHDA_CODEC_MAJOR_REV 0x0502
#define BIND_IHDA_CODEC_MINOR_REV 0x0503
#define BIND_IHDA_CODEC_VENDOR_REV 0x0504
#define BIND_IHDA_CODEC_VENDOR_STEP 0x0505
// Serial binding variables at 0x06XX
#define BIND_SERIAL_CLASS 0x0600
#define BIND_SERIAL_VID 0x0601
#define BIND_SERIAL_PID 0x0602
// NAND binding variables at 0x07XX
#define BIND_NAND_CLASS 0x0700
// Bluetooth binding variables at 0x08XX
#define BIND_BT_GATT_SVC_UUID16 0x0800
// 128-bit UUID is split across 4 32-bit unsigned ints
#define BIND_BT_GATT_SVC_UUID128_1 0x0801
#define BIND_BT_GATT_SVC_UUID128_2 0x0802
#define BIND_BT_GATT_SVC_UUID128_3 0x0803
#define BIND_BT_GATT_SVC_UUID128_4 0x0804
// SDIO binding variables at 0x09XX
#define BIND_SDIO_VID 0x0900
#define BIND_SDIO_PID 0x0901
// I2C binding variables at 0x0AXX
#define BIND_I2C_CLASS 0x0A00
// TEMPORARY binding variables at 0xfXX
// I2C_ADDR is a temporary way to bind the i2c touchscreen on the Acer12. This
// binding will eventually be made via some sort of ACPI device enumeration.
#define BIND_I2C_ADDR 0x0f00
typedef struct zx_bind_inst {
uint32_t op;
uint32_t arg;
} zx_bind_inst_t;
typedef struct zx_device_prop {
uint16_t id;
uint16_t reserved;
uint32_t value;
} zx_device_prop_t;
// simple example
#if 0
zx_bind_inst_t i915_binding[] = {
BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_PCI),
BI_ABORT_IF(NE, BIND_PCI_VID, 0x8086),
BI_MATCH_IF(EQ, BIND_PCI_DID, 0x1616), // broadwell
BI_MATCH_IF(EQ, BIND_PCI_DID, 0x1916), // skylake
BI_ABORT(),
};
#endif
#define ZIRCON_NOTE_NAME "Zircon"
#define ZIRCON_NOTE_DRIVER 0x31565244 // DRV1
typedef struct {
// Elf64_Nhdr fields:
uint32_t namesz;
uint32_t descsz;
uint32_t type;
// ELF note name. namesz is the exact size of the name (including '\0'),
// but the storage size is always rounded up to a multiple of 4 bytes.
char name[(sizeof(ZIRCON_NOTE_NAME) + 3) & -4];
} zircon_driver_note_header_t;
#define ZIRCON_DRIVER_NOTE_HEADER_INIT(object) { \
/* .namesz = */ sizeof(ZIRCON_NOTE_NAME), \
/* .descsz = */ (sizeof(object) - \
sizeof(zircon_driver_note_header_t)), \
/* .type = */ ZIRCON_NOTE_DRIVER, \
/* .name = */ ZIRCON_NOTE_NAME, \
}
typedef struct {
// See flag bits below.
uint32_t flags;
// Driver Metadata
uint32_t bindcount;
uint32_t reserved0;
char name[32];
char vendor[16];
char version[16];
// Driver Bind Program follows
} zircon_driver_note_payload_t;
// Flag bits in the driver note:
// Driver is built with `-fsanitize=address` and can only be loaded into a
// devhost that supports the ASan runtime.
#define ZIRCON_DRIVER_NOTE_FLAG_ASAN (1u << 0)
#define ZIRCON_DRIVER_NOTE_PAYLOAD_INIT(Driver,VendorName,Version,BindCount) \
{ \
/* .flags = */ ZIRCON_DRIVER_NOTE_FLAGS, \
/* .bindcount = */ (BindCount), \
/* .reserved0 = */ 0, \
/* .name = */ #Driver, \
/* .vendor = */ VendorName, \
/* .version = */ Version, \
}
#define ZIRCON_DRIVER_NOTE_FLAGS \
(__has_feature(address_sanitizer) ? ZIRCON_DRIVER_NOTE_FLAG_ASAN : 0)
typedef struct {
zircon_driver_note_header_t header;
zircon_driver_note_payload_t payload;
} zircon_driver_note_t;
static_assert(offsetof(zircon_driver_note_t, payload) ==
sizeof(zircon_driver_note_header_t),
"alignment snafu?");
// Without this, ASan will add redzone padding after the object, which
// would make it invalid ELF note format.
#if __has_feature(address_sanitizer)
# define ZIRCON_DRIVER_NOTE_ASAN __attribute__((no_sanitize("address")))
#else
# define ZIRCON_DRIVER_NOTE_ASAN
#endif
// GCC has a quirk about how '__attribute__((visibility("default")))'
// (__EXPORT here) works for const variables in C++. The attribute has no
// effect when used on the definition of a const variable, and GCC gives a
// warning/error about that. The attribute must appear on the "extern"
// declaration of the variable instead.
// We explicitly align the note to 4 bytes. That's its natural alignment
// anyway, but the compilers sometimes like to over-align as an
// optimization while other tools sometimes like to complain if SHT_NOTE
// sections are over-aligned (since this could result in padding being
// inserted that makes it violate the ELF note format). Standard C11
// doesn't permit alignas(...) on a type but we could use __ALIGNED(4) on
// all the types (i.e. GNU __attribute__ syntax instead of C11 syntax).
// But the alignment of the types is not actually the issue: it's the
// compiler deciding to over-align the individual object regardless of its
// type's alignment, so we have to explicitly set the alignment of the
// object to defeat any compiler default over-alignment.
#define ZIRCON_DRIVER_BEGIN(Driver,Ops,VendorName,Version,BindCount) \
zx_driver_rec_t __zircon_driver_rec__ __EXPORT = {\
/* .ops = */ &(Ops),\
/* .driver = */ NULL,\
/* .log_flags = */ 7, /* DDK_LOG_ERROR | DDK_LOG_WARN | DDK_LOG_INFO */\
};\
extern const struct zircon_driver_note __zircon_driver_note__ __EXPORT;\
alignas(4) __SECTION(".note.zircon.driver." #Driver) ZIRCON_DRIVER_NOTE_ASAN \
const struct zircon_driver_note {\
zircon_driver_note_t note;\
zx_bind_inst_t binding[BindCount];\
} __zircon_driver_note__ = {\
/* .note = */{\
ZIRCON_DRIVER_NOTE_HEADER_INIT(__zircon_driver_note__),\
ZIRCON_DRIVER_NOTE_PAYLOAD_INIT(Driver,VendorName,Version,BindCount),\
},\
/* .binding = */ {
#define ZIRCON_DRIVER_END(Driver) }};
//TODO: if we moved the Ops from the BEGIN() to END() macro we
// could add a zircon_driver_note_t* to the zx_driver_rec_t,
// define it in END(), and have only one symbol to dlsym()
// when loading drivers
__END_CDECLS;