// 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. #include #include #include #include #include "devcoordinator.h" typedef struct { const zx_device_prop_t* props; const zx_device_prop_t* end; uint32_t protocol_id; uint32_t binding_size; const zx_bind_inst_t* binding; const char* name; uint32_t autobind; } bpctx_t; static uint32_t dev_get_prop(bpctx_t* ctx, uint32_t id) { const zx_device_prop_t* props = ctx->props; const zx_device_prop_t* end = ctx->end; while (props < end) { if (props->id == id) { return props->value; } props++; } // fallback for devices without properties switch (id) { case BIND_PROTOCOL: return ctx->protocol_id; case BIND_AUTOBIND: return ctx->autobind; default: // TODO: better process for missing properties return 0; } } static bool is_bindable(bpctx_t* ctx) { const zx_bind_inst_t* ip = ctx->binding; const zx_bind_inst_t* end = ip + (ctx->binding_size / sizeof(zx_bind_inst_t)); uint32_t flags = 0; while (ip < end) { uint32_t inst = ip->op; bool cond; if (BINDINST_CC(inst) != COND_AL) { uint32_t value = ip->arg; uint32_t pid = BINDINST_PB(inst); uint32_t pval; if (pid != BIND_FLAGS) { pval = dev_get_prop(ctx, pid); } else { pval = flags; } // evaluate condition switch (BINDINST_CC(inst)) { case COND_EQ: cond = (pval == value); break; case COND_NE: cond = (pval != value); break; case COND_LT: cond = (pval < value); break; case COND_GT: cond = (pval > value); break; case COND_LE: cond = (pval <= value); break; case COND_GE: cond = (pval >= value); break; case COND_MASK: cond = ((pval & value) != 0); break; case COND_BITS: cond = ((pval & value) == value); break; default: // illegal instruction: abort printf("devmgr: driver '%s' illegal bindinst 0x%08x\n", ctx->name, inst); return false; } } else { cond = true; } if (cond) { switch (BINDINST_OP(inst)) { case OP_ABORT: return false; case OP_MATCH: return true; case OP_GOTO: { uint32_t label = BINDINST_PA(inst); while (++ip < end) { if ((BINDINST_OP(ip->op) == OP_LABEL) && (BINDINST_PA(ip->op) == label)) { goto next_instruction; } } printf("devmgr: driver '%s' illegal GOTO\n", ctx->name); return false; } case OP_SET: flags |= BINDINST_PA(inst); break; case OP_CLEAR: flags &= ~(BINDINST_PA(inst)); break; case OP_LABEL: // no op break; default: // illegal instruction: abort printf("devmgr: driver '%s' illegal bindinst 0x%08x\n", ctx->name, inst); return false; } } next_instruction: ip++; } // default if we leave the program is no-match return false; } bool dc_is_bindable(driver_t* drv, uint32_t protocol_id, zx_device_prop_t* props, size_t prop_count, bool autobind) { if (drv->binding_size == 0) { return false; } bpctx_t ctx; ctx.props = props; ctx.end = props + prop_count; ctx.protocol_id = protocol_id; ctx.binding = drv->binding; ctx.binding_size = drv->binding_size; ctx.name = drv->name; ctx.autobind = autobind ? 1 : 0; return is_bindable(&ctx); }