/* $OpenBSD: dsdt.c,v 1.242 2018/06/29 17:39:18 kettenis Exp $ */ /* * Copyright (c) 2005 Jordan Hargrave * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #ifdef DDB #include #endif #include #include #include #include #include #ifdef SMALL_KERNEL #undef ACPI_DEBUG #endif #define opsize(opcode) (((opcode) & 0xFF00) ? 2 : 1) #define AML_FIELD_RESERVED 0x00 #define AML_FIELD_ATTRIB 0x01 #define AML_REVISION 0x01 #define AML_INTSTRLEN 16 #define AML_NAMESEG_LEN 4 struct aml_value *aml_loadtable(struct acpi_softc *, const char *, const char *, const char *, const char *, const char *, struct aml_value *); struct aml_scope *aml_load(struct acpi_softc *, struct aml_scope *, struct aml_value *, struct aml_value *); void aml_copyvalue(struct aml_value *, struct aml_value *); void aml_setvalue(struct aml_scope *, struct aml_value *, struct aml_value *, int64_t); void aml_freevalue(struct aml_value *); struct aml_value *aml_allocvalue(int, int64_t, const void *); struct aml_value *_aml_setvalue(struct aml_value *, int, int64_t, const void *); uint64_t aml_convradix(uint64_t, int, int); uint64_t aml_evalexpr(uint64_t, uint64_t, int); int aml_lsb(uint64_t); int aml_msb(uint64_t); int aml_tstbit(const uint8_t *, int); void aml_setbit(uint8_t *, int, int); void aml_addref(struct aml_value *, const char *); void aml_delref(struct aml_value **, const char *); void aml_bufcpy(void *, int, const void *, int, int); int aml_pc(uint8_t *); struct aml_value *aml_parseop(struct aml_scope *, struct aml_value *,int); struct aml_value *aml_parsetarget(struct aml_scope *, struct aml_value *, struct aml_value **); struct aml_value *aml_parseterm(struct aml_scope *, struct aml_value *); struct aml_value *aml_evaltarget(struct aml_scope *scope, struct aml_value *res); int aml_evalterm(struct aml_scope *scope, struct aml_value *raw, struct aml_value *dst); struct aml_opcode *aml_findopcode(int); #define acpi_os_malloc(sz) _acpi_os_malloc(sz, __FUNCTION__, __LINE__) #define acpi_os_free(ptr) _acpi_os_free(ptr, __FUNCTION__, __LINE__) void *_acpi_os_malloc(size_t, const char *, int); void _acpi_os_free(void *, const char *, int); void acpi_stall(int); struct aml_value *aml_callosi(struct aml_scope *, struct aml_value *); const char *aml_getname(const char *); int64_t aml_hextoint(const char *); void aml_dump(int, uint8_t *); void _aml_die(const char *fn, int line, const char *fmt, ...); #define aml_die(x...) _aml_die(__FUNCTION__, __LINE__, x) void aml_notify_task(void *, int); void acpi_poll_notify_task(void *, int); extern char *hw_vendor; /* * @@@: Global variables */ int aml_intlen = 64; struct aml_node aml_root; struct aml_value *aml_global_lock; /* Perfect Hash key */ #define HASH_OFF 6904 #define HASH_SIZE 179 #define HASH_KEY(k) (((k) ^ HASH_OFF) % HASH_SIZE) /* * XXX this array should be sorted, and then aml_findopcode() should * do a binary search */ struct aml_opcode **aml_ophash; struct aml_opcode aml_table[] = { /* Simple types */ { AMLOP_ZERO, "Zero", "c", }, { AMLOP_ONE, "One", "c", }, { AMLOP_ONES, "Ones", "c", }, { AMLOP_REVISION, "Revision", "R", }, { AMLOP_BYTEPREFIX, ".Byte", "b", }, { AMLOP_WORDPREFIX, ".Word", "w", }, { AMLOP_DWORDPREFIX, ".DWord", "d", }, { AMLOP_QWORDPREFIX, ".QWord", "q", }, { AMLOP_STRINGPREFIX, ".String", "a", }, { AMLOP_DEBUG, "DebugOp", "D", }, { AMLOP_BUFFER, "Buffer", "piB", }, { AMLOP_PACKAGE, "Package", "pbT", }, { AMLOP_VARPACKAGE, "VarPackage", "piT", }, /* Simple objects */ { AMLOP_LOCAL0, "Local0", "L", }, { AMLOP_LOCAL1, "Local1", "L", }, { AMLOP_LOCAL2, "Local2", "L", }, { AMLOP_LOCAL3, "Local3", "L", }, { AMLOP_LOCAL4, "Local4", "L", }, { AMLOP_LOCAL5, "Local5", "L", }, { AMLOP_LOCAL6, "Local6", "L", }, { AMLOP_LOCAL7, "Local7", "L", }, { AMLOP_ARG0, "Arg0", "A", }, { AMLOP_ARG1, "Arg1", "A", }, { AMLOP_ARG2, "Arg2", "A", }, { AMLOP_ARG3, "Arg3", "A", }, { AMLOP_ARG4, "Arg4", "A", }, { AMLOP_ARG5, "Arg5", "A", }, { AMLOP_ARG6, "Arg6", "A", }, /* Control flow */ { AMLOP_IF, "If", "piI", }, { AMLOP_ELSE, "Else", "pT" }, { AMLOP_WHILE, "While", "piT", }, { AMLOP_BREAK, "Break", "" }, { AMLOP_CONTINUE, "Continue", "" }, { AMLOP_RETURN, "Return", "t", }, { AMLOP_FATAL, "Fatal", "bdi", }, { AMLOP_NOP, "Nop", "", }, { AMLOP_BREAKPOINT, "BreakPoint", "", }, /* Arithmetic operations */ { AMLOP_INCREMENT, "Increment", "S", }, { AMLOP_DECREMENT, "Decrement", "S", }, { AMLOP_ADD, "Add", "iir", }, { AMLOP_SUBTRACT, "Subtract", "iir", }, { AMLOP_MULTIPLY, "Multiply", "iir", }, { AMLOP_DIVIDE, "Divide", "iirr", }, { AMLOP_SHL, "ShiftLeft", "iir", }, { AMLOP_SHR, "ShiftRight", "iir", }, { AMLOP_AND, "And", "iir", }, { AMLOP_NAND, "Nand", "iir", }, { AMLOP_OR, "Or", "iir", }, { AMLOP_NOR, "Nor", "iir", }, { AMLOP_XOR, "Xor", "iir", }, { AMLOP_NOT, "Not", "ir", }, { AMLOP_MOD, "Mod", "iir", }, { AMLOP_FINDSETLEFTBIT, "FindSetLeftBit", "ir", }, { AMLOP_FINDSETRIGHTBIT,"FindSetRightBit", "ir",}, /* Logical test operations */ { AMLOP_LAND, "LAnd", "ii", }, { AMLOP_LOR, "LOr", "ii", }, { AMLOP_LNOT, "LNot", "i", }, { AMLOP_LNOTEQUAL, "LNotEqual", "tt", }, { AMLOP_LLESSEQUAL, "LLessEqual", "tt", }, { AMLOP_LGREATEREQUAL, "LGreaterEqual", "tt", }, { AMLOP_LEQUAL, "LEqual", "tt", }, { AMLOP_LGREATER, "LGreater", "tt", }, { AMLOP_LLESS, "LLess", "tt", }, /* Named objects */ { AMLOP_NAMECHAR, ".NameRef", "n", }, { AMLOP_ALIAS, "Alias", "nN", }, { AMLOP_NAME, "Name", "Nt", }, { AMLOP_EVENT, "Event", "N", }, { AMLOP_MUTEX, "Mutex", "Nb", }, { AMLOP_DATAREGION, "DataRegion", "Nttt", }, { AMLOP_OPREGION, "OpRegion", "Nbii", }, { AMLOP_SCOPE, "Scope", "pnT", }, { AMLOP_DEVICE, "Device", "pNT", }, { AMLOP_POWERRSRC, "Power Resource", "pNbwT",}, { AMLOP_THERMALZONE, "ThermalZone", "pNT", }, { AMLOP_PROCESSOR, "Processor", "pNbdbT", }, { AMLOP_METHOD, "Method", "pNbM", }, /* Field operations */ { AMLOP_FIELD, "Field", "pnbF", }, { AMLOP_INDEXFIELD, "IndexField", "pnnbF",}, { AMLOP_BANKFIELD, "BankField", "pnnibF",}, { AMLOP_CREATEFIELD, "CreateField", "tiiN", }, { AMLOP_CREATEQWORDFIELD, "CreateQWordField","tiN",}, { AMLOP_CREATEDWORDFIELD, "CreateDWordField","tiN",}, { AMLOP_CREATEWORDFIELD, "CreateWordField", "tiN",}, { AMLOP_CREATEBYTEFIELD, "CreateByteField", "tiN",}, { AMLOP_CREATEBITFIELD, "CreateBitField", "tiN", }, /* Conversion operations */ { AMLOP_TOINTEGER, "ToInteger", "tr", }, { AMLOP_TOBUFFER, "ToBuffer", "tr", }, { AMLOP_TODECSTRING, "ToDecString", "ir", }, { AMLOP_TOHEXSTRING, "ToHexString", "ir", }, { AMLOP_TOSTRING, "ToString", "tir", }, { AMLOP_MID, "Mid", "tiir", }, { AMLOP_FROMBCD, "FromBCD", "ir", }, { AMLOP_TOBCD, "ToBCD", "ir", }, /* Mutex/Signal operations */ { AMLOP_ACQUIRE, "Acquire", "Sw", }, { AMLOP_RELEASE, "Release", "S", }, { AMLOP_SIGNAL, "Signal", "S", }, { AMLOP_WAIT, "Wait", "Si", }, { AMLOP_RESET, "Reset", "S", }, { AMLOP_INDEX, "Index", "tir", }, { AMLOP_DEREFOF, "DerefOf", "t", }, { AMLOP_REFOF, "RefOf", "S", }, { AMLOP_CONDREFOF, "CondRef", "Sr", }, { AMLOP_LOADTABLE, "LoadTable", "tttttt" }, { AMLOP_STALL, "Stall", "i", }, { AMLOP_SLEEP, "Sleep", "i", }, { AMLOP_TIMER, "Timer", "", }, { AMLOP_LOAD, "Load", "nS", }, { AMLOP_UNLOAD, "Unload", "t" }, { AMLOP_STORE, "Store", "tS", }, { AMLOP_CONCAT, "Concat", "ttr", }, { AMLOP_CONCATRES, "ConcatRes", "ttt" }, { AMLOP_NOTIFY, "Notify", "Si", }, { AMLOP_SIZEOF, "Sizeof", "S", }, { AMLOP_MATCH, "Match", "tbibii", }, { AMLOP_OBJECTTYPE, "ObjectType", "S", }, { AMLOP_COPYOBJECT, "CopyObject", "tS", }, }; int aml_pc(uint8_t *src) { return src - aml_root.start; } struct aml_scope *aml_lastscope; void _aml_die(const char *fn, int line, const char *fmt, ...) { #ifndef SMALL_KERNEL struct aml_scope *root; struct aml_value *sp; int idx; #endif /* SMALL_KERNEL */ va_list ap; va_start(ap, fmt); vprintf(fmt, ap); printf("\n"); va_end(ap); #ifndef SMALL_KERNEL for (root = aml_lastscope; root && root->pos; root = root->parent) { printf("%.4x Called: %s\n", aml_pc(root->pos), aml_nodename(root->node)); for (idx = 0; idx < AML_MAX_ARG; idx++) { sp = aml_getstack(root, AMLOP_ARG0+idx); if (sp && sp->type) { printf(" arg%d: ", idx); aml_showvalue(sp); } } for (idx = 0; idx < AML_MAX_LOCAL; idx++) { sp = aml_getstack(root, AMLOP_LOCAL0+idx); if (sp && sp->type) { printf(" local%d: ", idx); aml_showvalue(sp); } } } #endif /* SMALL_KERNEL */ /* XXX: don't panic */ panic("aml_die %s:%d", fn, line); } void aml_hashopcodes(void) { int i; /* Dynamically allocate hash table */ aml_ophash = (struct aml_opcode **)acpi_os_malloc(HASH_SIZE * sizeof(struct aml_opcode *)); for (i = 0; i < sizeof(aml_table) / sizeof(aml_table[0]); i++) aml_ophash[HASH_KEY(aml_table[i].opcode)] = &aml_table[i]; } struct aml_opcode * aml_findopcode(int opcode) { struct aml_opcode *hop; hop = aml_ophash[HASH_KEY(opcode)]; if (hop && hop->opcode == opcode) return hop; return NULL; } #if defined(DDB) || !defined(SMALL_KERNEL) const char * aml_mnem(int opcode, uint8_t *pos) { struct aml_opcode *tab; static char mnemstr[32]; if ((tab = aml_findopcode(opcode)) != NULL) { strlcpy(mnemstr, tab->mnem, sizeof(mnemstr)); if (pos != NULL) { switch (opcode) { case AMLOP_STRINGPREFIX: snprintf(mnemstr, sizeof(mnemstr), "\"%s\"", pos); break; case AMLOP_BYTEPREFIX: snprintf(mnemstr, sizeof(mnemstr), "0x%.2x", *(uint8_t *)pos); break; case AMLOP_WORDPREFIX: snprintf(mnemstr, sizeof(mnemstr), "0x%.4x", *(uint16_t *)pos); break; case AMLOP_DWORDPREFIX: snprintf(mnemstr, sizeof(mnemstr), "0x%.4x", *(uint16_t *)pos); break; case AMLOP_NAMECHAR: strlcpy(mnemstr, aml_getname(pos), sizeof(mnemstr)); break; } } return mnemstr; } return ("xxx"); } #endif /* defined(DDB) || !defined(SMALL_KERNEL) */ struct aml_notify_data { struct aml_node *node; char pnpid[20]; void *cbarg; int (*cbproc)(struct aml_node *, int, void *); int poll; SLIST_ENTRY(aml_notify_data) link; }; SLIST_HEAD(aml_notify_head, aml_notify_data); struct aml_notify_head aml_notify_list = SLIST_HEAD_INITIALIZER(aml_notify_list); /* * @@@: Memory management functions */ long acpi_nalloc; struct acpi_memblock { size_t size; #ifdef ACPI_MEMDEBUG const char *fn; int line; int sig; LIST_ENTRY(acpi_memblock) link; #endif }; #ifdef ACPI_MEMDEBUG LIST_HEAD(, acpi_memblock) acpi_memhead; int acpi_memsig; int acpi_walkmem(int sig, const char *lbl) { struct acpi_memblock *sptr; printf("--- walkmem:%s %x --- %lx bytes alloced\n", lbl, sig, acpi_nalloc); LIST_FOREACH(sptr, &acpi_memhead, link) { if (sptr->sig < sig) break; printf("%.4x Alloc %.8lx bytes @ %s:%d\n", sptr->sig, sptr->size, sptr->fn, sptr->line); } return acpi_memsig; } #endif /* ACPI_MEMDEBUG */ void * _acpi_os_malloc(size_t size, const char *fn, int line) { struct acpi_memblock *sptr; sptr = malloc(size+sizeof(*sptr), M_ACPI, M_WAITOK | M_ZERO); dnprintf(99, "alloc: %p %s:%d\n", sptr, fn, line); acpi_nalloc += size; sptr->size = size; #ifdef ACPI_MEMDEBUG sptr->line = line; sptr->fn = fn; sptr->sig = ++acpi_memsig; LIST_INSERT_HEAD(&acpi_memhead, sptr, link); #endif return &sptr[1]; } void _acpi_os_free(void *ptr, const char *fn, int line) { struct acpi_memblock *sptr; if (ptr != NULL) { sptr = &(((struct acpi_memblock *)ptr)[-1]); acpi_nalloc -= sptr->size; #ifdef ACPI_MEMDEBUG LIST_REMOVE(sptr, link); #endif dnprintf(99, "free: %p %s:%d\n", sptr, fn, line); free(sptr, M_ACPI, sizeof(*sptr) + sptr->size); } } void acpi_sleep(int ms, char *reason) { static int acpinowait; int to = ms * hz / 1000; if (cold) delay(ms * 1000); else { if (to <= 0) to = 1; tsleep(&acpinowait, PWAIT, reason, to); } } void acpi_stall(int us) { delay(us); } /* * @@@: Misc utility functions */ #ifdef ACPI_DEBUG void aml_dump(int len, uint8_t *buf) { int idx; dnprintf(50, "{ "); for (idx = 0; idx < len; idx++) { dnprintf(50, "%s0x%.2x", idx ? ", " : "", buf[idx]); } dnprintf(50, " }\n"); } #endif /* Bit mangling code */ int aml_tstbit(const uint8_t *pb, int bit) { pb += aml_bytepos(bit); return (*pb & aml_bitmask(bit)); } void aml_setbit(uint8_t *pb, int bit, int val) { pb += aml_bytepos(bit); if (val) *pb |= aml_bitmask(bit); else *pb &= ~aml_bitmask(bit); } /* * @@@: Notify functions */ void acpi_poll(void *arg) { int s; s = spltty(); acpi_addtask(acpi_softc, acpi_poll_notify_task, NULL, 0); acpi_softc->sc_threadwaiting = 0; wakeup(acpi_softc); splx(s); timeout_add_sec(&acpi_softc->sc_dev_timeout, 10); } void aml_notify_task(void *node, int notify_value) { struct aml_notify_data *pdata = NULL; dnprintf(10,"run notify: %s %x\n", aml_nodename(node), notify_value); SLIST_FOREACH(pdata, &aml_notify_list, link) if (pdata->node == node) pdata->cbproc(pdata->node, notify_value, pdata->cbarg); } void aml_register_notify(struct aml_node *node, const char *pnpid, int (*proc)(struct aml_node *, int, void *), void *arg, int poll) { struct aml_notify_data *pdata; extern int acpi_poll_enabled; dnprintf(10, "aml_register_notify: %s %s %p\n", node->name, pnpid ? pnpid : "", proc); pdata = acpi_os_malloc(sizeof(struct aml_notify_data)); pdata->node = node; pdata->cbarg = arg; pdata->cbproc = proc; pdata->poll = poll; if (pnpid) strlcpy(pdata->pnpid, pnpid, sizeof(pdata->pnpid)); SLIST_INSERT_HEAD(&aml_notify_list, pdata, link); if (poll && !acpi_poll_enabled) timeout_add_sec(&acpi_softc->sc_dev_timeout, 10); } void aml_notify(struct aml_node *node, int notify_value) { if (node == NULL) return; dnprintf(10,"queue notify: %s %x\n", aml_nodename(node), notify_value); acpi_addtask(acpi_softc, aml_notify_task, node, notify_value); } void aml_notify_dev(const char *pnpid, int notify_value) { struct aml_notify_data *pdata = NULL; if (pnpid == NULL) return; SLIST_FOREACH(pdata, &aml_notify_list, link) if (strcmp(pdata->pnpid, pnpid) == 0) pdata->cbproc(pdata->node, notify_value, pdata->cbarg); } void acpi_poll_notify_task(void *arg0, int arg1) { struct aml_notify_data *pdata = NULL; SLIST_FOREACH(pdata, &aml_notify_list, link) if (pdata->cbproc && pdata->poll) pdata->cbproc(pdata->node, 0, pdata->cbarg); } /* * @@@: Namespace functions */ struct aml_node *__aml_search(struct aml_node *, uint8_t *, int); struct aml_node *__aml_searchname(struct aml_node *, const void *, int); void aml_delchildren(struct aml_node *); /* Search for a name in children nodes */ struct aml_node * __aml_search(struct aml_node *root, uint8_t *nameseg, int create) { struct aml_node *node; /* XXX: Replace with SLIST/SIMPLEQ routines */ if (root == NULL) return NULL; SIMPLEQ_FOREACH(node, &root->son, sib) { if (!strncmp(node->name, nameseg, AML_NAMESEG_LEN)) return node; } if (create) { node = acpi_os_malloc(sizeof(struct aml_node)); memcpy((void *)node->name, nameseg, AML_NAMESEG_LEN); node->value = aml_allocvalue(0,0,NULL); node->value->node = node; node->parent = root; SIMPLEQ_INIT(&node->son); SIMPLEQ_INSERT_TAIL(&root->son, node, sib); } return node; } /* Get absolute pathname of AML node */ const char * aml_nodename(struct aml_node *node) { static char namebuf[128]; namebuf[0] = 0; if (node) { aml_nodename(node->parent); if (node->parent != &aml_root) strlcat(namebuf, ".", sizeof(namebuf)); strlcat(namebuf, node->name, sizeof(namebuf)); return namebuf+1; } return namebuf; } const char * aml_getname(const char *name) { static char namebuf[128], *p; int count; p = namebuf; while (*name == AMLOP_ROOTCHAR || *name == AMLOP_PARENTPREFIX) *(p++) = *(name++); switch (*name) { case 0x00: count = 0; break; case AMLOP_MULTINAMEPREFIX: count = name[1]; name += 2; break; case AMLOP_DUALNAMEPREFIX: count = 2; name += 1; break; default: count = 1; } while (count--) { memcpy(p, name, 4); p[4] = '.'; p += 5; name += 4; if (*name == '.') name++; } *(--p) = 0; return namebuf; } /* Free all children nodes/values */ void aml_delchildren(struct aml_node *node) { struct aml_node *onode; if (node == NULL) return; while ((onode = SIMPLEQ_FIRST(&node->son)) != NULL) { SIMPLEQ_REMOVE_HEAD(&node->son, sib); aml_delchildren(onode); /* Don't delete values that have references */ if (onode->value && onode->value->refcnt > 1) onode->value->node = NULL; /* Decrease reference count */ aml_delref(&onode->value, ""); /* Delete node */ acpi_os_free(onode); } } /* * @@@: Value functions */ /* * Field I/O code */ void aml_unlockfield(struct aml_scope *, struct aml_value *); void aml_lockfield(struct aml_scope *, struct aml_value *); static long global_lock_count = 0; void acpi_glk_enter(void) { int st = 0; /* If lock is already ours, just continue. */ if (global_lock_count++) return; /* Spin to acquire the lock. */ while (!st) { st = acpi_acquire_glk(&acpi_softc->sc_facs->global_lock); /* XXX - yield/delay? */ } } void acpi_glk_leave(void) { int st, x; /* If we are the last one, turn out the lights. */ if (--global_lock_count) return; st = acpi_release_glk(&acpi_softc->sc_facs->global_lock); if (!st) return; /* * If pending, notify the BIOS that the lock was released by * OSPM. No locking is needed because nobody outside the ACPI * thread is supposed to touch this register. */ x = acpi_read_pmreg(acpi_softc, ACPIREG_PM1_CNT, 0); x |= ACPI_PM1_GBL_RLS; acpi_write_pmreg(acpi_softc, ACPIREG_PM1_CNT, 0, x); } void aml_lockfield(struct aml_scope *scope, struct aml_value *field) { if (AML_FIELD_LOCK(field->v_field.flags) != AML_FIELD_LOCK_ON) return; acpi_glk_enter(); } void aml_unlockfield(struct aml_scope *scope, struct aml_value *field) { if (AML_FIELD_LOCK(field->v_field.flags) != AML_FIELD_LOCK_ON) return; acpi_glk_leave(); } /* * @@@: Value set/compare/alloc/free routines */ #ifndef SMALL_KERNEL void aml_showvalue(struct aml_value *val) { int idx; if (val == NULL) return; if (val->node) printf(" [%s]", aml_nodename(val->node)); printf(" %p cnt:%.2x stk:%.2x", val, val->refcnt, val->stack); switch (val->type) { case AML_OBJTYPE_INTEGER: printf(" integer: %llx\n", val->v_integer); break; case AML_OBJTYPE_STRING: printf(" string: %s\n", val->v_string); break; case AML_OBJTYPE_METHOD: printf(" method: %.2x\n", val->v_method.flags); break; case AML_OBJTYPE_PACKAGE: printf(" package: %.2x\n", val->length); for (idx = 0; idx < val->length; idx++) aml_showvalue(val->v_package[idx]); break; case AML_OBJTYPE_BUFFER: printf(" buffer: %.2x {", val->length); for (idx = 0; idx < val->length; idx++) printf("%s%.2x", idx ? ", " : "", val->v_buffer[idx]); printf("}\n"); break; case AML_OBJTYPE_FIELDUNIT: case AML_OBJTYPE_BUFFERFIELD: printf(" field: bitpos=%.4x bitlen=%.4x ref1:%p ref2:%p [%s]\n", val->v_field.bitpos, val->v_field.bitlen, val->v_field.ref1, val->v_field.ref2, aml_mnem(val->v_field.type, NULL)); if (val->v_field.ref1) printf(" ref1: %s\n", aml_nodename(val->v_field.ref1->node)); if (val->v_field.ref2) printf(" ref2: %s\n", aml_nodename(val->v_field.ref2->node)); break; case AML_OBJTYPE_MUTEX: printf(" mutex: %s ref: %d\n", val->v_mutex ? val->v_mutex->amt_name : "", val->v_mutex ? val->v_mutex->amt_ref_count : 0); break; case AML_OBJTYPE_EVENT: printf(" event:\n"); break; case AML_OBJTYPE_OPREGION: printf(" opregion: %.2x,%.8llx,%x\n", val->v_opregion.iospace, val->v_opregion.iobase, val->v_opregion.iolen); break; case AML_OBJTYPE_NAMEREF: printf(" nameref: %s\n", aml_getname(val->v_nameref)); break; case AML_OBJTYPE_DEVICE: printf(" device:\n"); break; case AML_OBJTYPE_PROCESSOR: printf(" cpu: %.2x,%.4x,%.2x\n", val->v_processor.proc_id, val->v_processor.proc_addr, val->v_processor.proc_len); break; case AML_OBJTYPE_THERMZONE: printf(" thermzone:\n"); break; case AML_OBJTYPE_POWERRSRC: printf(" pwrrsrc: %.2x,%.2x\n", val->v_powerrsrc.pwr_level, val->v_powerrsrc.pwr_order); break; case AML_OBJTYPE_OBJREF: printf(" objref: %p index:%x opcode:%s\n", val->v_objref.ref, val->v_objref.index, aml_mnem(val->v_objref.type, 0)); aml_showvalue(val->v_objref.ref); break; default: printf(" !!type: %x\n", val->type); } } #endif /* SMALL_KERNEL */ int64_t aml_val2int(struct aml_value *rval) { int64_t ival = 0; if (rval == NULL) { dnprintf(50, "null val2int\n"); return (0); } switch (rval->type) { case AML_OBJTYPE_INTEGER: ival = rval->v_integer; break; case AML_OBJTYPE_BUFFER: aml_bufcpy(&ival, 0, rval->v_buffer, 0, min(aml_intlen, rval->length*8)); break; case AML_OBJTYPE_STRING: ival = aml_hextoint(rval->v_string); break; } return (ival); } /* Sets value into LHS: lhs must already be cleared */ struct aml_value * _aml_setvalue(struct aml_value *lhs, int type, int64_t ival, const void *bval) { memset(&lhs->_, 0x0, sizeof(lhs->_)); lhs->type = type; switch (lhs->type) { case AML_OBJTYPE_INTEGER: lhs->length = aml_intlen>>3; lhs->v_integer = ival; break; case AML_OBJTYPE_METHOD: lhs->v_method.flags = ival; lhs->v_method.fneval = bval; break; case AML_OBJTYPE_NAMEREF: lhs->v_nameref = (uint8_t *)bval; break; case AML_OBJTYPE_OBJREF: lhs->v_objref.type = ival; lhs->v_objref.ref = (struct aml_value *)bval; break; case AML_OBJTYPE_BUFFER: lhs->length = ival; lhs->v_buffer = (uint8_t *)acpi_os_malloc(ival); if (bval) memcpy(lhs->v_buffer, bval, ival); break; case AML_OBJTYPE_STRING: if (ival == -1) ival = strlen((const char *)bval); lhs->length = ival; lhs->v_string = (char *)acpi_os_malloc(ival+1); if (bval) strncpy(lhs->v_string, (const char *)bval, ival); break; case AML_OBJTYPE_PACKAGE: lhs->length = ival; lhs->v_package = (struct aml_value **)acpi_os_malloc(ival * sizeof(struct aml_value *)); for (ival = 0; ival < lhs->length; ival++) lhs->v_package[ival] = aml_allocvalue( AML_OBJTYPE_UNINITIALIZED, 0, NULL); break; } return lhs; } /* Copy object to another value: lhs must already be cleared */ void aml_copyvalue(struct aml_value *lhs, struct aml_value *rhs) { int idx; lhs->type = rhs->type; switch (lhs->type) { case AML_OBJTYPE_UNINITIALIZED: break; case AML_OBJTYPE_INTEGER: lhs->length = aml_intlen>>3; lhs->v_integer = rhs->v_integer; break; case AML_OBJTYPE_MUTEX: lhs->v_mutex = rhs->v_mutex; break; case AML_OBJTYPE_POWERRSRC: lhs->v_powerrsrc = rhs->v_powerrsrc; break; case AML_OBJTYPE_METHOD: lhs->v_method = rhs->v_method; break; case AML_OBJTYPE_BUFFER: _aml_setvalue(lhs, rhs->type, rhs->length, rhs->v_buffer); break; case AML_OBJTYPE_STRING: _aml_setvalue(lhs, rhs->type, rhs->length, rhs->v_string); break; case AML_OBJTYPE_OPREGION: lhs->v_opregion = rhs->v_opregion; break; case AML_OBJTYPE_PROCESSOR: lhs->v_processor = rhs->v_processor; break; case AML_OBJTYPE_NAMEREF: lhs->v_nameref = rhs->v_nameref; break; case AML_OBJTYPE_PACKAGE: _aml_setvalue(lhs, rhs->type, rhs->length, NULL); for (idx = 0; idx < rhs->length; idx++) aml_copyvalue(lhs->v_package[idx], rhs->v_package[idx]); break; case AML_OBJTYPE_OBJREF: lhs->v_objref = rhs->v_objref; aml_addref(lhs->v_objref.ref, ""); break; default: printf("copyvalue: %x", rhs->type); break; } } /* Allocate dynamic AML value * type : Type of object to allocate (AML_OBJTYPE_XXXX) * ival : Integer value (action depends on type) * bval : Buffer value (action depends on type) */ struct aml_value * aml_allocvalue(int type, int64_t ival, const void *bval) { struct aml_value *rv; rv = (struct aml_value *)acpi_os_malloc(sizeof(struct aml_value)); if (rv != NULL) { aml_addref(rv, ""); return _aml_setvalue(rv, type, ival, bval); } return NULL; } void aml_freevalue(struct aml_value *val) { int idx; if (val == NULL) return; switch (val->type) { case AML_OBJTYPE_STRING: acpi_os_free(val->v_string); break; case AML_OBJTYPE_BUFFER: acpi_os_free(val->v_buffer); break; case AML_OBJTYPE_PACKAGE: for (idx = 0; idx < val->length; idx++) { aml_freevalue(val->v_package[idx]); acpi_os_free(val->v_package[idx]); } acpi_os_free(val->v_package); break; case AML_OBJTYPE_OBJREF: aml_delref(&val->v_objref.ref, ""); break; case AML_OBJTYPE_BUFFERFIELD: case AML_OBJTYPE_FIELDUNIT: aml_delref(&val->v_field.ref1, ""); aml_delref(&val->v_field.ref2, ""); break; } val->type = 0; memset(&val->_, 0, sizeof(val->_)); } /* * @@@: Math eval routines */ /* Convert number from one radix to another * Used in BCD conversion routines */ uint64_t aml_convradix(uint64_t val, int iradix, int oradix) { uint64_t rv = 0, pwr; rv = 0; pwr = 1; while (val) { rv += (val % iradix) * pwr; val /= iradix; pwr *= oradix; } return rv; } /* Calculate LSB */ int aml_lsb(uint64_t val) { int lsb; if (val == 0) return (0); for (lsb = 1; !(val & 0x1); lsb++) val >>= 1; return (lsb); } /* Calculate MSB */ int aml_msb(uint64_t val) { int msb; if (val == 0) return (0); for (msb = 1; val != 0x1; msb++) val >>= 1; return (msb); } /* Evaluate Math operands */ uint64_t aml_evalexpr(uint64_t lhs, uint64_t rhs, int opcode) { uint64_t res = 0; switch (opcode) { /* Math operations */ case AMLOP_INCREMENT: case AMLOP_ADD: res = (lhs + rhs); break; case AMLOP_DECREMENT: case AMLOP_SUBTRACT: res = (lhs - rhs); break; case AMLOP_MULTIPLY: res = (lhs * rhs); break; case AMLOP_DIVIDE: res = (lhs / rhs); break; case AMLOP_MOD: res = (lhs % rhs); break; case AMLOP_SHL: res = (lhs << rhs); break; case AMLOP_SHR: res = (lhs >> rhs); break; case AMLOP_AND: res = (lhs & rhs); break; case AMLOP_NAND: res = ~(lhs & rhs); break; case AMLOP_OR: res = (lhs | rhs); break; case AMLOP_NOR: res = ~(lhs | rhs); break; case AMLOP_XOR: res = (lhs ^ rhs); break; case AMLOP_NOT: res = ~(lhs); break; /* Conversion/misc */ case AMLOP_FINDSETLEFTBIT: res = aml_msb(lhs); break; case AMLOP_FINDSETRIGHTBIT: res = aml_lsb(lhs); break; case AMLOP_TOINTEGER: res = (lhs); break; case AMLOP_FROMBCD: res = aml_convradix(lhs, 16, 10); break; case AMLOP_TOBCD: res = aml_convradix(lhs, 10, 16); break; /* Logical/Comparison */ case AMLOP_LAND: res = -(lhs && rhs); break; case AMLOP_LOR: res = -(lhs || rhs); break; case AMLOP_LNOT: res = -(!lhs); break; case AMLOP_LNOTEQUAL: res = -(lhs != rhs); break; case AMLOP_LLESSEQUAL: res = -(lhs <= rhs); break; case AMLOP_LGREATEREQUAL: res = -(lhs >= rhs); break; case AMLOP_LEQUAL: res = -(lhs == rhs); break; case AMLOP_LGREATER: res = -(lhs > rhs); break; case AMLOP_LLESS: res = -(lhs < rhs); break; } dnprintf(15,"aml_evalexpr: %s %llx %llx = %llx\n", aml_mnem(opcode, NULL), lhs, rhs, res); return res; } /* * aml_bufcpy copies/shifts buffer data, special case for aligned transfers * dstPos/srcPos are bit positions within destination/source buffers */ void aml_bufcpy(void *pvDst, int dstPos, const void *pvSrc, int srcPos, int len) { const uint8_t *pSrc = pvSrc; uint8_t *pDst = pvDst; int idx; if (aml_bytealigned(dstPos|srcPos|len)) { /* Aligned transfer: use memcpy */ memcpy(pDst+aml_bytepos(dstPos), pSrc+aml_bytepos(srcPos), aml_bytelen(len)); return; } /* Misaligned transfer: perform bitwise copy (slow) */ for (idx = 0; idx < len; idx++) aml_setbit(pDst, idx + dstPos, aml_tstbit(pSrc, idx + srcPos)); } /* * @@@: External API * * evaluate an AML node * Returns a copy of the value in res (must be freed by user) */ void aml_walknodes(struct aml_node *node, int mode, int (*nodecb)(struct aml_node *, void *), void *arg) { struct aml_node *child; if (node == NULL) return; if (mode == AML_WALK_PRE) if (nodecb(node, arg)) return; SIMPLEQ_FOREACH(child, &node->son, sib) aml_walknodes(child, mode, nodecb, arg); if (mode == AML_WALK_POST) nodecb(node, arg); } void aml_find_node(struct aml_node *node, const char *name, int (*cbproc)(struct aml_node *, void *arg), void *arg) { struct aml_node *child; const char *nn; SIMPLEQ_FOREACH(child, &node->son, sib) { nn = child->name; if (nn != NULL) { if (*nn == AMLOP_ROOTCHAR) nn++; while (*nn == AMLOP_PARENTPREFIX) nn++; if (strcmp(name, nn) == 0) { /* Only recurse if cbproc() wants us to */ if (cbproc(child, arg) == 0) continue; } } aml_find_node(child, name, cbproc, arg); } } /* * @@@: Parser functions */ uint8_t *aml_parsename(struct aml_node *, uint8_t *, struct aml_value **, int); uint8_t *aml_parseend(struct aml_scope *scope); int aml_parselength(struct aml_scope *); int aml_parseopcode(struct aml_scope *); /* Get AML Opcode */ int aml_parseopcode(struct aml_scope *scope) { int opcode = (scope->pos[0]); int twocode = (scope->pos[0]<<8) + scope->pos[1]; /* Check if this is an embedded name */ switch (opcode) { case AMLOP_ROOTCHAR: case AMLOP_PARENTPREFIX: case AMLOP_MULTINAMEPREFIX: case AMLOP_DUALNAMEPREFIX: case AMLOP_NAMECHAR: return AMLOP_NAMECHAR; } if (opcode >= 'A' && opcode <= 'Z') return AMLOP_NAMECHAR; if (twocode == AMLOP_LNOTEQUAL || twocode == AMLOP_LLESSEQUAL || twocode == AMLOP_LGREATEREQUAL || opcode == AMLOP_EXTPREFIX) { scope->pos += 2; return twocode; } scope->pos += 1; return opcode; } /* Decode embedded AML Namestring */ uint8_t * aml_parsename(struct aml_node *inode, uint8_t *pos, struct aml_value **rval, int create) { struct aml_node *relnode, *node = inode; uint8_t *start = pos; int i; if (*pos == AMLOP_ROOTCHAR) { pos++; node = &aml_root; } while (*pos == AMLOP_PARENTPREFIX) { pos++; if ((node = node->parent) == NULL) node = &aml_root; } switch (*pos) { case 0x00: pos++; break; case AMLOP_MULTINAMEPREFIX: for (i=0; iparent; } while (!node && pos == start && relnode); pos += AML_NAMESEG_LEN; break; } if (node) { *rval = node->value; /* Dereference ALIAS here */ if ((*rval)->type == AML_OBJTYPE_OBJREF && (*rval)->v_objref.type == AMLOP_ALIAS) { dnprintf(10, "deref alias: %s\n", aml_nodename(node)); *rval = (*rval)->v_objref.ref; } aml_addref(*rval, 0); dnprintf(10, "parsename: %s %x\n", aml_nodename(node), (*rval)->type); } else { *rval = aml_allocvalue(AML_OBJTYPE_NAMEREF, 0, start); dnprintf(10, "%s:%s not found\n", aml_nodename(inode), aml_getname(start)); } return pos; } /* Decode AML Length field * AML Length field is encoded: * byte0 byte1 byte2 byte3 * 00xxxxxx : if upper bits == 00, length = xxxxxx * 01--xxxx yyyyyyyy : if upper bits == 01, length = yyyyyyyyxxxx * 10--xxxx yyyyyyyy zzzzzzzz : if upper bits == 10, length = zzzzzzzzyyyyyyyyxxxx * 11--xxxx yyyyyyyy zzzzzzzz wwwwwwww : if upper bits == 11, length = wwwwwwwwzzzzzzzzyyyyyyyyxxxx */ int aml_parselength(struct aml_scope *scope) { int len; uint8_t lcode; lcode = *(scope->pos++); if (lcode <= 0x3F) return lcode; /* lcode >= 0x40, multibyte length, get first byte of extended length */ len = lcode & 0xF; len += *(scope->pos++) << 4L; if (lcode >= 0x80) len += *(scope->pos++) << 12L; if (lcode >= 0xC0) len += *(scope->pos++) << 20L; return len; } /* Get address of end of scope; based on current address */ uint8_t * aml_parseend(struct aml_scope *scope) { uint8_t *pos = scope->pos; int len; len = aml_parselength(scope); if (pos+len > scope->end) { dnprintf(10, "Bad scope... runover pos:%.4x new end:%.4x scope " "end:%.4x\n", aml_pc(pos), aml_pc(pos+len), aml_pc(scope->end)); pos = scope->end; } return pos+len; } /* * @@@: Opcode utility functions */ /* * @@@: Opcode functions */ int odp; const char hext[] = "0123456789ABCDEF"; const char * aml_eisaid(uint32_t pid) { static char id[8]; id[0] = '@' + ((pid >> 2) & 0x1F); id[1] = '@' + ((pid << 3) & 0x18) + ((pid >> 13) & 0x7); id[2] = '@' + ((pid >> 8) & 0x1F); id[3] = hext[(pid >> 20) & 0xF]; id[4] = hext[(pid >> 16) & 0xF]; id[5] = hext[(pid >> 28) & 0xF]; id[6] = hext[(pid >> 24) & 0xF]; id[7] = 0; return id; } /* * @@@: Default Object creation */ static char osstring[] = "Macrosift Windogs MT"; struct aml_defval { const char *name; int type; int64_t ival; const void *bval; struct aml_value **gval; } aml_defobj[] = { { "_OS_", AML_OBJTYPE_STRING, -1, osstring }, { "_REV", AML_OBJTYPE_INTEGER, 2, NULL }, { "_GL", AML_OBJTYPE_MUTEX, 1, NULL, &aml_global_lock }, { "_OSI", AML_OBJTYPE_METHOD, 1, aml_callosi }, /* Create default scopes */ { "_GPE" }, { "_PR_" }, { "_SB_" }, { "_TZ_" }, { "_SI_" }, { NULL } }; /* _OSI Default Method: * Returns True if string argument matches list of known OS strings * We return True for Windows to fake out nasty bad AML */ char *aml_valid_osi[] = { "Windows 2000", "Windows 2001", "Windows 2001.1", "Windows 2001.1 SP1", "Windows 2001 SP0", "Windows 2001 SP1", "Windows 2001 SP2", "Windows 2001 SP3", "Windows 2001 SP4", "Windows 2006", "Windows 2006.1", "Windows 2006 SP1", "Windows 2006 SP2", "Windows 2009", "Windows 2012", "Windows 2013", "Windows 2015", NULL }; struct aml_value * aml_callosi(struct aml_scope *scope, struct aml_value *val) { int idx, result=0; struct aml_value *fa; fa = aml_getstack(scope, AMLOP_ARG0); if (hw_vendor != NULL && (strcmp(hw_vendor, "Apple Inc.") == 0 || strcmp(hw_vendor, "Apple Computer, Inc.") == 0)) { if (strcmp(fa->v_string, "Darwin") == 0) { dnprintf(10,"osi: returning 1 for %s on %s hardware\n", fa->v_string, hw_vendor); result = 1; } else dnprintf(10,"osi: on %s hardware, but ignoring %s\n", hw_vendor, fa->v_string); return aml_allocvalue(AML_OBJTYPE_INTEGER, result, NULL); } for (idx=0; !result && aml_valid_osi[idx] != NULL; idx++) { dnprintf(10,"osi: %s,%s\n", fa->v_string, aml_valid_osi[idx]); result = !strcmp(fa->v_string, aml_valid_osi[idx]); } dnprintf(10,"@@ OSI found: %x\n", result); return aml_allocvalue(AML_OBJTYPE_INTEGER, result, NULL); } void aml_create_defaultobjects(void) { struct aml_value *tmp; struct aml_defval *def; #ifdef ACPI_MEMDEBUG LIST_INIT(&acpi_memhead); #endif osstring[1] = 'i'; osstring[6] = 'o'; osstring[15] = 'w'; osstring[18] = 'N'; SIMPLEQ_INIT(&aml_root.son); strlcpy(aml_root.name, "\\", sizeof(aml_root.name)); aml_root.value = aml_allocvalue(0, 0, NULL); aml_root.value->node = &aml_root; for (def = aml_defobj; def->name; def++) { /* Allocate object value + add to namespace */ aml_parsename(&aml_root, (uint8_t *)def->name, &tmp, 1); _aml_setvalue(tmp, def->type, def->ival, def->bval); if (def->gval) { /* Set root object pointer */ *def->gval = tmp; } aml_delref(&tmp, 0); } } #ifdef ACPI_DEBUG int aml_print_resource(union acpi_resource *crs, void *arg) { int typ = AML_CRSTYPE(crs); switch (typ) { case LR_EXTIRQ: printf("extirq\tflags:%.2x len:%.2x irq:%.4x\n", crs->lr_extirq.flags, crs->lr_extirq.irq_count, letoh32(crs->lr_extirq.irq[0])); break; case SR_IRQ: printf("irq\t%.4x %.2x\n", letoh16(crs->sr_irq.irq_mask), crs->sr_irq.irq_flags); break; case SR_DMA: printf("dma\t%.2x %.2x\n", crs->sr_dma.channel, crs->sr_dma.flags); break; case SR_IOPORT: printf("ioport\tflags:%.2x _min:%.4x _max:%.4x _aln:%.2x _len:%.2x\n", crs->sr_ioport.flags, crs->sr_ioport._min, crs->sr_ioport._max, crs->sr_ioport._aln, crs->sr_ioport._len); break; case SR_STARTDEP: printf("startdep\n"); break; case SR_ENDDEP: printf("enddep\n"); break; case LR_WORD: printf("word\ttype:%.2x flags:%.2x tflag:%.2x gra:%.4x min:%.4x max:%.4x tra:%.4x len:%.4x\n", crs->lr_word.type, crs->lr_word.flags, crs->lr_word.tflags, crs->lr_word._gra, crs->lr_word._min, crs->lr_word._max, crs->lr_word._tra, crs->lr_word._len); break; case LR_DWORD: printf("dword\ttype:%.2x flags:%.2x tflag:%.2x gra:%.8x min:%.8x max:%.8x tra:%.8x len:%.8x\n", crs->lr_dword.type, crs->lr_dword.flags, crs->lr_dword.tflags, crs->lr_dword._gra, crs->lr_dword._min, crs->lr_dword._max, crs->lr_dword._tra, crs->lr_dword._len); break; case LR_QWORD: printf("dword\ttype:%.2x flags:%.2x tflag:%.2x gra:%.16llx min:%.16llx max:%.16llx tra:%.16llx len:%.16llx\n", crs->lr_qword.type, crs->lr_qword.flags, crs->lr_qword.tflags, crs->lr_qword._gra, crs->lr_qword._min, crs->lr_qword._max, crs->lr_qword._tra, crs->lr_qword._len); break; default: printf("unknown type: %x\n", typ); break; } return (0); } #endif /* ACPI_DEBUG */ union acpi_resource *aml_mapresource(union acpi_resource *); union acpi_resource * aml_mapresource(union acpi_resource *crs) { static union acpi_resource map; int rlen; rlen = AML_CRSLEN(crs); if (rlen >= sizeof(map)) return crs; memset(&map, 0, sizeof(map)); memcpy(&map, crs, rlen); return ↦ } int aml_parse_resource(struct aml_value *res, int (*crs_enum)(int, union acpi_resource *, void *), void *arg) { int off, rlen, crsidx; union acpi_resource *crs; if (res->type != AML_OBJTYPE_BUFFER || res->length < 5) return (-1); for (off = 0, crsidx = 0; off < res->length; off += rlen, crsidx++) { crs = (union acpi_resource *)(res->v_buffer+off); rlen = AML_CRSLEN(crs); if (crs->hdr.typecode == SRT_ENDTAG || !rlen) break; crs = aml_mapresource(crs); #ifdef ACPI_DEBUG aml_print_resource(crs, NULL); #endif crs_enum(crsidx, crs, arg); } return (0); } void aml_foreachpkg(struct aml_value *pkg, int start, void (*fn)(struct aml_value *, void *), void *arg) { int idx; if (pkg->type != AML_OBJTYPE_PACKAGE) return; for (idx=start; idxlength; idx++) fn(pkg->v_package[idx], arg); } /* * Walk nodes and perform fixups for nameref */ int aml_fixup_node(struct aml_node *, void *); int aml_fixup_node(struct aml_node *node, void *arg) { struct aml_value *val = arg; int i; if (node->value == NULL) return (0); if (arg == NULL) aml_fixup_node(node, node->value); else if (val->type == AML_OBJTYPE_NAMEREF) { node = aml_searchname(node, val->v_nameref); if (node && node->value) { _aml_setvalue(val, AML_OBJTYPE_OBJREF, AMLOP_NAMECHAR, node->value); } } else if (val->type == AML_OBJTYPE_PACKAGE) { for (i = 0; i < val->length; i++) aml_fixup_node(node, val->v_package[i]); } return (0); } void aml_postparse(void) { aml_walknodes(&aml_root, AML_WALK_PRE, aml_fixup_node, NULL); } #ifndef SMALL_KERNEL const char * aml_val_to_string(const struct aml_value *val) { static char buffer[256]; int len; switch (val->type) { case AML_OBJTYPE_BUFFER: len = val->length; if (len >= sizeof(buffer)) len = sizeof(buffer) - 1; memcpy(buffer, val->v_buffer, len); buffer[len] = 0; break; case AML_OBJTYPE_STRING: strlcpy(buffer, val->v_string, sizeof(buffer)); break; case AML_OBJTYPE_INTEGER: snprintf(buffer, sizeof(buffer), "%llx", val->v_integer); break; default: snprintf(buffer, sizeof(buffer), "Failed to convert type %d to string!", val->type); }; return (buffer); } #endif /* SMALL_KERNEL */ int aml_error; struct aml_value *aml_gettgt(struct aml_value *, int); struct aml_value *aml_eval(struct aml_scope *, struct aml_value *, int, int, struct aml_value *); struct aml_value *aml_parsesimple(struct aml_scope *, char, struct aml_value *); struct aml_value *aml_parse(struct aml_scope *, int, const char *); struct aml_value *aml_seterror(struct aml_scope *, const char *, ...); struct aml_scope *aml_findscope(struct aml_scope *, int, int); struct aml_scope *aml_pushscope(struct aml_scope *, struct aml_value *, struct aml_node *, int); struct aml_scope *aml_popscope(struct aml_scope *); void aml_showstack(struct aml_scope *); struct aml_value *aml_convert(struct aml_value *, int, int); int aml_matchtest(int64_t, int64_t, int); int aml_match(struct aml_value *, int, int, int, int, int); int aml_compare(struct aml_value *, struct aml_value *, int); struct aml_value *aml_concat(struct aml_value *, struct aml_value *); struct aml_value *aml_concatres(struct aml_value *, struct aml_value *); struct aml_value *aml_mid(struct aml_value *, int, int); int aml_ccrlen(int, union acpi_resource *, void *); void aml_store(struct aml_scope *, struct aml_value *, int64_t, struct aml_value *); /* * Reference Count functions */ void aml_addref(struct aml_value *val, const char *lbl) { if (val == NULL) return; dnprintf(50, "XAddRef: %p %s:[%s] %d\n", val, lbl, val->node ? aml_nodename(val->node) : "INTERNAL", val->refcnt); val->refcnt++; } /* Decrease reference counter */ void aml_delref(struct aml_value **pv, const char *lbl) { struct aml_value *val; if (pv == NULL || *pv == NULL) return; val = *pv; val->refcnt--; if (val->refcnt == 0) { dnprintf(50, "XDelRef: %p %s %2d [%s] %s\n", val, lbl, val->refcnt, val->node ? aml_nodename(val->node) : "INTERNAL", val->refcnt ? "" : "---------------- FREEING"); aml_freevalue(val); acpi_os_free(val); *pv = NULL; } } /* Walk list of parent scopes until we find one of 'type' * If endscope is set, mark all intermediate scopes as invalid (used for Method/While) */ struct aml_scope * aml_findscope(struct aml_scope *scope, int type, int endscope) { while (scope) { switch (endscope) { case AMLOP_RETURN: scope->pos = scope->end; if (scope->type == AMLOP_WHILE) scope->pos = NULL; break; case AMLOP_CONTINUE: scope->pos = scope->end; break; case AMLOP_BREAK: scope->pos = scope->end; if (scope->type == type) scope->parent->pos = scope->end; break; } if (scope->type == type) break; scope = scope->parent; } return scope; } struct aml_value * aml_getstack(struct aml_scope *scope, int opcode) { struct aml_value *sp; sp = NULL; scope = aml_findscope(scope, AMLOP_METHOD, 0); if (scope == NULL) return NULL; if (opcode >= AMLOP_LOCAL0 && opcode <= AMLOP_LOCAL7) { if (scope->locals == NULL) scope->locals = aml_allocvalue(AML_OBJTYPE_PACKAGE, 8, NULL); sp = scope->locals->v_package[opcode - AMLOP_LOCAL0]; sp->stack = opcode; } else if (opcode >= AMLOP_ARG0 && opcode <= AMLOP_ARG6) { if (scope->args == NULL) scope->args = aml_allocvalue(AML_OBJTYPE_PACKAGE, 7, NULL); sp = scope->args->v_package[opcode - AMLOP_ARG0]; if (sp->type == AML_OBJTYPE_OBJREF) sp = sp->v_objref.ref; } return sp; } #ifdef ACPI_DEBUG /* Dump AML Stack */ void aml_showstack(struct aml_scope *scope) { struct aml_value *sp; int idx; dnprintf(10, "===== Stack %s:%s\n", aml_nodename(scope->node), aml_mnem(scope->type, 0)); for (idx=0; scope->args && idx<7; idx++) { sp = aml_getstack(scope, AMLOP_ARG0+idx); if (sp && sp->type) { dnprintf(10," Arg%d: ", idx); aml_showvalue(sp); } } for (idx=0; scope->locals && idx<8; idx++) { sp = aml_getstack(scope, AMLOP_LOCAL0+idx); if (sp && sp->type) { dnprintf(10," Local%d: ", idx); aml_showvalue(sp); } } } #endif /* Create a new scope object */ struct aml_scope * aml_pushscope(struct aml_scope *parent, struct aml_value *range, struct aml_node *node, int type) { struct aml_scope *scope; uint8_t *start, *end; if (range->type == AML_OBJTYPE_METHOD) { start = range->v_method.start; end = range->v_method.end; } else { start = range->v_buffer; end = start + range->length; if (start == end) return NULL; } scope = acpi_os_malloc(sizeof(struct aml_scope)); if (scope == NULL) return NULL; scope->node = node; scope->start = start; scope->end = end; scope->pos = scope->start; scope->parent = parent; scope->type = type; scope->sc = acpi_softc; if (parent) scope->depth = parent->depth+1; aml_lastscope = scope; return scope; } /* Free a scope object and any children */ struct aml_scope * aml_popscope(struct aml_scope *scope) { struct aml_scope *nscope; if (scope == NULL) return NULL; nscope = scope->parent; if (scope->type == AMLOP_METHOD) aml_delchildren(scope->node); if (scope->locals) { aml_freevalue(scope->locals); acpi_os_free(scope->locals); scope->locals = NULL; } if (scope->args) { aml_freevalue(scope->args); acpi_os_free(scope->args); scope->args = NULL; } acpi_os_free(scope); aml_lastscope = nscope; return nscope; } /* Test AMLOP_MATCH codes */ int aml_matchtest(int64_t a, int64_t b, int op) { switch (op) { case AML_MATCH_TR: return (1); case AML_MATCH_EQ: return (a == b); case AML_MATCH_LT: return (a < b); case AML_MATCH_LE: return (a <= b); case AML_MATCH_GE: return (a >= b); case AML_MATCH_GT: return (a > b); } return (0); } /* Search a package for a matching value */ int aml_match(struct aml_value *pkg, int index, int op1, int v1, int op2, int v2) { struct aml_value *tmp; int flag; while (index < pkg->length) { /* Convert package value to integer */ tmp = aml_convert(pkg->v_package[index], AML_OBJTYPE_INTEGER, -1); /* Perform test */ flag = aml_matchtest(tmp->v_integer, v1, op1) && aml_matchtest(tmp->v_integer, v2, op2); aml_delref(&tmp, "xmatch"); if (flag) return index; index++; } return -1; } /* * Conversion routines */ int64_t aml_hextoint(const char *str) { int64_t v = 0; char c; while (*str) { if (*str >= '0' && *str <= '9') c = *(str++) - '0'; else if (*str >= 'a' && *str <= 'f') c = *(str++) - 'a' + 10; else if (*str >= 'A' && *str <= 'F') c = *(str++) - 'A' + 10; else break; v = (v << 4) + c; } return v; } struct aml_value * aml_convert(struct aml_value *a, int ctype, int clen) { struct aml_value *c = NULL; /* Object is already this type */ if (clen == -1) clen = a->length; if (a->type == ctype) { aml_addref(a, "XConvert"); return a; } switch (ctype) { case AML_OBJTYPE_BUFFER: dnprintf(10,"convert to buffer\n"); switch (a->type) { case AML_OBJTYPE_INTEGER: c = aml_allocvalue(AML_OBJTYPE_BUFFER, a->length, &a->v_integer); break; case AML_OBJTYPE_STRING: c = aml_allocvalue(AML_OBJTYPE_BUFFER, a->length, a->v_string); break; } break; case AML_OBJTYPE_INTEGER: dnprintf(10,"convert to integer : %x\n", a->type); switch (a->type) { case AML_OBJTYPE_BUFFER: c = aml_allocvalue(AML_OBJTYPE_INTEGER, 0, NULL); memcpy(&c->v_integer, a->v_buffer, min(a->length, c->length)); break; case AML_OBJTYPE_STRING: c = aml_allocvalue(AML_OBJTYPE_INTEGER, 0, NULL); c->v_integer = aml_hextoint(a->v_string); break; case AML_OBJTYPE_UNINITIALIZED: c = aml_allocvalue(AML_OBJTYPE_INTEGER, 0, NULL); break; } break; case AML_OBJTYPE_STRING: case AML_OBJTYPE_HEXSTRING: case AML_OBJTYPE_DECSTRING: dnprintf(10,"convert to string\n"); switch (a->type) { case AML_OBJTYPE_INTEGER: c = aml_allocvalue(AML_OBJTYPE_STRING, 20, NULL); snprintf(c->v_string, c->length, (ctype == AML_OBJTYPE_HEXSTRING) ? "0x%llx" : "%lld", a->v_integer); break; case AML_OBJTYPE_BUFFER: c = aml_allocvalue(AML_OBJTYPE_STRING, a->length, a->v_buffer); break; case AML_OBJTYPE_STRING: aml_addref(a, "XConvert"); return a; } break; } if (c == NULL) { #ifndef SMALL_KERNEL aml_showvalue(a); #endif aml_die("Could not convert %x to %x\n", a->type, ctype); } return c; } int aml_compare(struct aml_value *a1, struct aml_value *a2, int opcode) { int rc = 0; /* Convert A2 to type of A1 */ a2 = aml_convert(a2, a1->type, -1); if (a1->type == AML_OBJTYPE_INTEGER) rc = aml_evalexpr(a1->v_integer, a2->v_integer, opcode); else { /* Perform String/Buffer comparison */ rc = memcmp(a1->v_buffer, a2->v_buffer, min(a1->length, a2->length)); if (rc == 0) { /* If buffers match, which one is longer */ rc = a1->length - a2->length; } /* Perform comparison against zero */ rc = aml_evalexpr(rc, 0, opcode); } /* Either deletes temp buffer, or decrease refcnt on original A2 */ aml_delref(&a2, "xcompare"); return rc; } /* Concatenate two objects, returning pointer to new object */ struct aml_value * aml_concat(struct aml_value *a1, struct aml_value *a2) { struct aml_value *c = NULL; /* Convert arg2 to type of arg1 */ a2 = aml_convert(a2, a1->type, -1); switch (a1->type) { case AML_OBJTYPE_INTEGER: c = aml_allocvalue(AML_OBJTYPE_BUFFER, a1->length + a2->length, NULL); memcpy(c->v_buffer, &a1->v_integer, a1->length); memcpy(c->v_buffer+a1->length, &a2->v_integer, a2->length); break; case AML_OBJTYPE_BUFFER: c = aml_allocvalue(AML_OBJTYPE_BUFFER, a1->length + a2->length, NULL); memcpy(c->v_buffer, a1->v_buffer, a1->length); memcpy(c->v_buffer+a1->length, a2->v_buffer, a2->length); break; case AML_OBJTYPE_STRING: c = aml_allocvalue(AML_OBJTYPE_STRING, a1->length + a2->length, NULL); memcpy(c->v_string, a1->v_string, a1->length); memcpy(c->v_string+a1->length, a2->v_string, a2->length); break; default: aml_die("concat type mismatch %d != %d\n", a1->type, a2->type); break; } /* Either deletes temp buffer, or decrease refcnt on original A2 */ aml_delref(&a2, "xconcat"); return c; } /* Calculate length of Resource Template */ int aml_ccrlen(int crsidx, union acpi_resource *rs, void *arg) { int *plen = arg; *plen += AML_CRSLEN(rs); return (0); } /* Concatenate resource templates, returning pointer to new object */ struct aml_value * aml_concatres(struct aml_value *a1, struct aml_value *a2) { struct aml_value *c; int l1 = 0, l2 = 0, l3 = 2; uint8_t a3[] = { SRT_ENDTAG, 0x00 }; if (a1->type != AML_OBJTYPE_BUFFER || a2->type != AML_OBJTYPE_BUFFER) aml_die("concatres: not buffers\n"); /* Walk a1, a2, get length minus end tags, concatenate buffers, add end tag */ aml_parse_resource(a1, aml_ccrlen, &l1); aml_parse_resource(a2, aml_ccrlen, &l2); /* Concatenate buffers, add end tag */ c = aml_allocvalue(AML_OBJTYPE_BUFFER, l1+l2+l3, NULL); memcpy(c->v_buffer, a1->v_buffer, l1); memcpy(c->v_buffer+l1, a2->v_buffer, l2); memcpy(c->v_buffer+l1+l2, a3, l3); return c; } /* Extract substring from string or buffer */ struct aml_value * aml_mid(struct aml_value *src, int index, int length) { if (index > src->length) index = src->length; if ((index + length) > src->length) length = src->length - index; return aml_allocvalue(src->type, length, src->v_buffer + index); } /* * Field I/O utility functions */ void aml_createfield(struct aml_value *, int, struct aml_value *, int, int, struct aml_value *, int, int); void aml_parsefieldlist(struct aml_scope *, int, int, struct aml_value *, struct aml_value *, int); int aml_evalhid(struct aml_node *node, struct aml_value *val) { if (aml_evalname(acpi_softc, node, "_HID", 0, NULL, val)) return (-1); /* Integer _HID: convert to EISA ID */ if (val->type == AML_OBJTYPE_INTEGER) _aml_setvalue(val, AML_OBJTYPE_STRING, -1, aml_eisaid(val->v_integer)); return (0); } int aml_opreg_sysmem_handler(void *cookie, int iodir, uint64_t address, int size, uint64_t *value) { return acpi_gasio(acpi_softc, iodir, GAS_SYSTEM_MEMORY, address, size, size, value); } int aml_opreg_sysio_handler(void *cookie, int iodir, uint64_t address, int size, uint64_t *value) { return acpi_gasio(acpi_softc, iodir, GAS_SYSTEM_IOSPACE, address, size, size, value); } int aml_opreg_pcicfg_handler(void *cookie, int iodir, uint64_t address, int size, uint64_t *value) { return acpi_gasio(acpi_softc, iodir, GAS_PCI_CFG_SPACE, address, size, size, value); } int aml_opreg_ec_handler(void *cookie, int iodir, uint64_t address, int size, uint64_t *value) { return acpi_gasio(acpi_softc, iodir, GAS_EMBEDDED, address, size, size, value); } struct aml_regionspace { void *cookie; int (*handler)(void *, int, uint64_t, int, uint64_t *); }; struct aml_regionspace aml_regionspace[256] = { [ACPI_OPREG_SYSMEM] = { NULL, aml_opreg_sysmem_handler }, [ACPI_OPREG_SYSIO] = { NULL, aml_opreg_sysio_handler }, [ACPI_OPREG_PCICFG] = { NULL, aml_opreg_pcicfg_handler }, [ACPI_OPREG_EC] = { NULL, aml_opreg_ec_handler }, }; void aml_register_regionspace(struct aml_node *node, int iospace, void *cookie, int (*handler)(void *, int, uint64_t, int, uint64_t *)) { struct aml_value arg[2]; KASSERT(iospace >= 0 && iospace < 256); aml_regionspace[iospace].cookie = cookie; aml_regionspace[iospace].handler = handler; /* Register address space. */ memset(&arg, 0, sizeof(arg)); arg[0].type = AML_OBJTYPE_INTEGER; arg[0].v_integer = iospace; arg[1].type = AML_OBJTYPE_INTEGER; arg[1].v_integer = 1; node = aml_searchname(node, "_REG"); if (node) aml_evalnode(acpi_softc, node, 2, arg, NULL); } void aml_rwgen(struct aml_value *, int, int, struct aml_value *, int, int); void aml_rwgpio(struct aml_value *, int, int, struct aml_value *, int, int); void aml_rwgsb(struct aml_value *, int, int, int, struct aml_value *, int, int); void aml_rwindexfield(struct aml_value *, struct aml_value *val, int); void aml_rwfield(struct aml_value *, int, int, struct aml_value *, int); /* Get PCI address for opregion objects */ int aml_rdpciaddr(struct aml_node *pcidev, union amlpci_t *addr) { int64_t res; if (aml_evalinteger(acpi_softc, pcidev, "_ADR", 0, NULL, &res) == 0) { addr->fun = res & 0xFFFF; addr->dev = res >> 16; } while (pcidev != NULL) { /* HID device (PCI or PCIE root): eval _BBN */ if (__aml_search(pcidev, "_HID", 0)) { if (aml_evalinteger(acpi_softc, pcidev, "_BBN", 0, NULL, &res) == 0) { addr->bus = res; break; } } pcidev = pcidev->parent; } return (0); } int acpi_genio(struct acpi_softc *sc, int iodir, int iospace, uint64_t address, int access_size, int len, void *buffer) { struct aml_regionspace *region = &aml_regionspace[iospace]; uint8_t *pb; int reg; dnprintf(50, "genio: %.2x 0x%.8llx %s\n", iospace, address, (iodir == ACPI_IOWRITE) ? "write" : "read"); KASSERT((len % access_size) == 0); pb = (uint8_t *)buffer; for (reg = 0; reg < len; reg += access_size) { uint64_t value; int err; if (iodir == ACPI_IOREAD) { err = region->handler(region->cookie, iodir, address + reg, access_size, &value); if (err) return err; switch (access_size) { case 1: *(uint8_t *)(pb + reg) = value; break; case 2: *(uint16_t *)(pb + reg) = value; break; case 4: *(uint32_t *)(pb + reg) = value; break; default: printf("%s: invalid access size %d on read\n", __func__, access_size); return -1; } } else { switch (access_size) { case 1: value = *(uint8_t *)(pb + reg); break; case 2: value = *(uint16_t *)(pb + reg); break; case 4: value = *(uint32_t *)(pb + reg); break; default: printf("%s: invalid access size %d on write\n", __func__, access_size); return -1; } err = region->handler(region->cookie, iodir, address + reg, access_size, &value); if (err) return err; } } return 0; } /* Read/Write from opregion object */ void aml_rwgen(struct aml_value *rgn, int bpos, int blen, struct aml_value *val, int mode, int flag) { struct aml_value tmp; union amlpci_t pi; void *tbit, *vbit; int tlen, type, sz; dnprintf(10," %5s %.2x %.8llx %.4x [%s]\n", mode == ACPI_IOREAD ? "read" : "write", rgn->v_opregion.iospace, rgn->v_opregion.iobase + (bpos >> 3), blen, aml_nodename(rgn->node)); memset(&tmp, 0, sizeof(tmp)); /* Get field access size */ switch (AML_FIELD_ACCESS(flag)) { case AML_FIELD_WORDACC: sz = 2; break; case AML_FIELD_DWORDACC: sz = 4; break; case AML_FIELD_QWORDACC: sz = 8; break; default: sz = 1; break; } pi.addr = (rgn->v_opregion.iobase + (bpos >> 3)) & ~(sz - 1); bpos += ((rgn->v_opregion.iobase & (sz - 1)) << 3); bpos &= ((sz << 3) - 1); if (rgn->v_opregion.iospace == ACPI_OPREG_PCICFG) { /* Get PCI Root Address for this opregion */ aml_rdpciaddr(rgn->node->parent, &pi); } tbit = &tmp.v_integer; vbit = &val->v_integer; tlen = roundup(bpos + blen, sz << 3); type = rgn->v_opregion.iospace; if (aml_regionspace[type].handler == NULL) { printf("%s: unregistered RegionSpace 0x%x\n", __func__, type); return; } /* Allocate temporary storage */ if (tlen > aml_intlen) { _aml_setvalue(&tmp, AML_OBJTYPE_BUFFER, tlen >> 3, 0); tbit = tmp.v_buffer; } if (blen > aml_intlen) { if (mode == ACPI_IOREAD) { /* Read from a large field: create buffer */ _aml_setvalue(val, AML_OBJTYPE_BUFFER, (blen + 7) >> 3, 0); } else { /* Write to a large field.. create or convert buffer */ val = aml_convert(val, AML_OBJTYPE_BUFFER, -1); if (blen > (val->length << 3)) blen = val->length << 3; } vbit = val->v_buffer; } else { if (mode == ACPI_IOREAD) { /* Read from a short field.. initialize integer */ _aml_setvalue(val, AML_OBJTYPE_INTEGER, 0, 0); } else { /* Write to a short field.. convert to integer */ val = aml_convert(val, AML_OBJTYPE_INTEGER, -1); } } if (mode == ACPI_IOREAD) { /* Read bits from opregion */ acpi_genio(acpi_softc, ACPI_IOREAD, type, pi.addr, sz, tlen >> 3, tbit); aml_bufcpy(vbit, 0, tbit, bpos, blen); } else { /* Write bits to opregion */ if (AML_FIELD_UPDATE(flag) == AML_FIELD_PRESERVE && (bpos != 0 || blen != tlen)) { acpi_genio(acpi_softc, ACPI_IOREAD, type, pi.addr, sz, tlen >> 3, tbit); } else if (AML_FIELD_UPDATE(flag) == AML_FIELD_WRITEASONES) { memset(tbit, 0xff, tmp.length); } /* Copy target bits, then write to region */ aml_bufcpy(tbit, bpos, vbit, 0, blen); acpi_genio(acpi_softc, ACPI_IOWRITE, type, pi.addr, sz, tlen >> 3, tbit); aml_delref(&val, "fld.write"); } aml_freevalue(&tmp); } void aml_rwgpio(struct aml_value *conn, int bpos, int blen, struct aml_value *val, int mode, int flag) { union acpi_resource *crs = (union acpi_resource *)conn->v_buffer; struct aml_node *node; uint16_t pin; int v = 0; if (conn->type != AML_OBJTYPE_BUFFER || conn->length < 5 || AML_CRSTYPE(crs) != LR_GPIO || AML_CRSLEN(crs) > conn->length) aml_die("Invalid GpioIo"); if (bpos != 0 || blen != 1) aml_die("Invalid GpioIo access"); node = aml_searchname(conn->node, (char *)&crs->pad[crs->lr_gpio.res_off]); pin = *(uint16_t *)&crs->pad[crs->lr_gpio.pin_off]; if (node == NULL || node->gpio == NULL) aml_die("Could not find GpioIo pin"); if (mode == ACPI_IOWRITE) { v = aml_val2int(val); node->gpio->write_pin(node->gpio->cookie, pin, v); } else { v = node->gpio->read_pin(node->gpio->cookie, pin); _aml_setvalue(val, AML_OBJTYPE_INTEGER, v, NULL); } } #ifndef SMALL_KERNEL void aml_rwgsb(struct aml_value *conn, int alen, int bpos, int blen, struct aml_value *val, int mode, int flag) { union acpi_resource *crs = (union acpi_resource *)conn->v_buffer; struct aml_node *node; i2c_tag_t tag; i2c_op_t op; i2c_addr_t addr; int cmdlen, buflen; uint8_t cmd; uint8_t *buf; int err; if (conn->type != AML_OBJTYPE_BUFFER || conn->length < 5 || AML_CRSTYPE(crs) != LR_SERBUS || AML_CRSLEN(crs) > conn->length || crs->lr_i2cbus.revid != 1 || crs->lr_i2cbus.type != LR_SERBUS_I2C) aml_die("Invalid GenericSerialBus"); if (AML_FIELD_ACCESS(flag) != AML_FIELD_BUFFERACC || bpos & 0x3 || blen != 8) aml_die("Invalid GenericSerialBus access"); node = aml_searchname(conn->node, (char *)&crs->lr_i2cbus.vdata[crs->lr_i2cbus.tlength - 6]); if (node == NULL || node->i2c == NULL) aml_die("Could not find GenericSerialBus controller"); switch (((flag >> 6) & 0x3)) { case 0: /* Normal */ switch (AML_FIELD_ATTR(flag)) { case 0x02: /* AttribQuick */ cmdlen = 0; buflen = 0; break; case 0x04: /* AttribSendReceive */ cmdlen = 0; buflen = 1; break; case 0x06: /* AttribByte */ cmdlen = 1; buflen = 1; break; case 0x08: /* AttribWord */ cmdlen = 1; buflen = 2; break; case 0x0b: /* AttribBytes */ cmdlen = 1; buflen = alen; break; case 0x0e: /* AttribRawBytes */ cmdlen = 0; buflen = alen; break; default: aml_die("unsupported access type 0x%x", flag); break; } break; case 1: /* AttribBytes */ cmdlen = 1; buflen = AML_FIELD_ATTR(flag); break; case 2: /* AttribRawBytes */ cmdlen = 0; buflen = AML_FIELD_ATTR(flag); break; default: aml_die("unsupported access type 0x%x", flag); break; } if (mode == ACPI_IOREAD) { _aml_setvalue(val, AML_OBJTYPE_BUFFER, buflen + 2, NULL); op = I2C_OP_READ_WITH_STOP; } else { op = I2C_OP_WRITE_WITH_STOP; } tag = node->i2c; addr = crs->lr_i2cbus._adr; cmd = bpos >> 3; buf = val->v_buffer; iic_acquire_bus(tag, 0); err = iic_exec(tag, op, addr, &cmd, cmdlen, &buf[2], buflen, 0); iic_release_bus(tag, 0); /* * The ACPI specification doesn't tell us what the status * codes mean beyond implying that zero means success. So use * the error returned from the transfer. All possible error * numbers should fit in a single byte. */ buf[0] = err; } #endif void aml_rwindexfield(struct aml_value *fld, struct aml_value *val, int mode) { struct aml_value tmp, *ref1, *ref2; void *tbit, *vbit; int vpos, bpos, blen; int indexval; int sz, len; ref2 = fld->v_field.ref2; ref1 = fld->v_field.ref1; bpos = fld->v_field.bitpos; blen = fld->v_field.bitlen; memset(&tmp, 0, sizeof(tmp)); tmp.refcnt = 99; /* Get field access size */ switch (AML_FIELD_ACCESS(fld->v_field.flags)) { case AML_FIELD_WORDACC: sz = 2; break; case AML_FIELD_DWORDACC: sz = 4; break; case AML_FIELD_QWORDACC: sz = 8; break; default: sz = 1; break; } if (blen > aml_intlen) { if (mode == ACPI_IOREAD) { /* Read from a large field: create buffer */ _aml_setvalue(val, AML_OBJTYPE_BUFFER, (blen + 7) >> 3, 0); } vbit = val->v_buffer; } else { if (mode == ACPI_IOREAD) { /* Read from a short field: initialize integer */ _aml_setvalue(val, AML_OBJTYPE_INTEGER, 0, 0); } vbit = &val->v_integer; } tbit = &tmp.v_integer; vpos = 0; indexval = (bpos >> 3) & ~(sz - 1); bpos = bpos - (indexval << 3); while (blen > 0) { len = min(blen, (sz << 3) - bpos); /* Write index register */ _aml_setvalue(&tmp, AML_OBJTYPE_INTEGER, indexval, 0); aml_rwfield(ref2, 0, aml_intlen, &tmp, ACPI_IOWRITE); indexval += sz; /* Read/write data register */ _aml_setvalue(&tmp, AML_OBJTYPE_INTEGER, 0, 0); if (mode == ACPI_IOWRITE) aml_bufcpy(tbit, 0, vbit, vpos, len); aml_rwfield(ref1, bpos, len, &tmp, mode); if (mode == ACPI_IOREAD) aml_bufcpy(vbit, vpos, tbit, 0, len); vpos += len; blen -= len; bpos = 0; } } void aml_rwfield(struct aml_value *fld, int bpos, int blen, struct aml_value *val, int mode) { struct aml_value tmp, *ref1, *ref2; ref2 = fld->v_field.ref2; ref1 = fld->v_field.ref1; if (blen > fld->v_field.bitlen) blen = fld->v_field.bitlen; aml_lockfield(NULL, fld); memset(&tmp, 0, sizeof(tmp)); aml_addref(&tmp, "fld.write"); if (fld->v_field.type == AMLOP_INDEXFIELD) { aml_rwindexfield(fld, val, mode); } else if (fld->v_field.type == AMLOP_BANKFIELD) { _aml_setvalue(&tmp, AML_OBJTYPE_INTEGER, fld->v_field.ref3, 0); aml_rwfield(ref2, 0, aml_intlen, &tmp, ACPI_IOWRITE); aml_rwgen(ref1, fld->v_field.bitpos, fld->v_field.bitlen, val, mode, fld->v_field.flags); } else if (fld->v_field.type == AMLOP_FIELD) { switch (ref1->v_opregion.iospace) { case ACPI_OPREG_GPIO: aml_rwgpio(ref2, bpos, blen, val, mode, fld->v_field.flags); break; #ifndef SMALL_KERNEL case ACPI_OPREG_GSB: aml_rwgsb(ref2, fld->v_field.ref3, fld->v_field.bitpos + bpos, blen, val, mode, fld->v_field.flags); break; #endif default: aml_rwgen(ref1, fld->v_field.bitpos + bpos, blen, val, mode, fld->v_field.flags); break; } } else if (mode == ACPI_IOREAD) { /* bufferfield:read */ _aml_setvalue(val, AML_OBJTYPE_INTEGER, 0, 0); aml_bufcpy(&val->v_integer, 0, ref1->v_buffer, fld->v_field.bitpos, fld->v_field.bitlen); } else { /* bufferfield:write */ val = aml_convert(val, AML_OBJTYPE_INTEGER, -1); aml_bufcpy(ref1->v_buffer, fld->v_field.bitpos, &val->v_integer, 0, fld->v_field.bitlen); aml_delref(&val, "wrbuffld"); } aml_unlockfield(NULL, fld); } /* Create Field Object data index * AMLOP_FIELD n:OpRegion NULL * AMLOP_INDEXFIELD n:Field n:Field * AMLOP_BANKFIELD n:OpRegion n:Field * AMLOP_CREATEFIELD t:Buffer NULL * AMLOP_CREATEBITFIELD t:Buffer NULL * AMLOP_CREATEBYTEFIELD t:Buffer NULL * AMLOP_CREATEWORDFIELD t:Buffer NULL * AMLOP_CREATEDWORDFIELD t:Buffer NULL * AMLOP_CREATEQWORDFIELD t:Buffer NULL * AMLOP_INDEX t:Buffer NULL */ void aml_createfield(struct aml_value *field, int opcode, struct aml_value *data, int bpos, int blen, struct aml_value *index, int indexval, int flags) { dnprintf(10, "## %s(%s): %s %.4x-%.4x\n", aml_mnem(opcode, 0), blen > aml_intlen ? "BUF" : "INT", aml_nodename(field->node), bpos, blen); if (index) { dnprintf(10, " index:%s:%.2x\n", aml_nodename(index->node), indexval); } dnprintf(10, " data:%s\n", aml_nodename(data->node)); field->type = (opcode == AMLOP_FIELD || opcode == AMLOP_INDEXFIELD || opcode == AMLOP_BANKFIELD) ? AML_OBJTYPE_FIELDUNIT : AML_OBJTYPE_BUFFERFIELD; if (field->type == AML_OBJTYPE_BUFFERFIELD && data->type != AML_OBJTYPE_BUFFER) data = aml_convert(data, AML_OBJTYPE_BUFFER, -1); field->v_field.type = opcode; field->v_field.bitpos = bpos; field->v_field.bitlen = blen; field->v_field.ref3 = indexval; field->v_field.ref2 = index; field->v_field.ref1 = data; field->v_field.flags = flags; /* Increase reference count */ aml_addref(data, "Field.Data"); aml_addref(index, "Field.Index"); } /* Parse Field/IndexField/BankField scope */ void aml_parsefieldlist(struct aml_scope *mscope, int opcode, int flags, struct aml_value *data, struct aml_value *index, int indexval) { struct aml_value *conn = NULL; struct aml_value *rv; int bpos, blen; if (mscope == NULL) return; bpos = 0; while (mscope->pos < mscope->end) { switch (*mscope->pos) { case 0x00: /* ReservedField */ mscope->pos++; blen = aml_parselength(mscope); break; case 0x01: /* AccessField */ mscope->pos++; blen = 0; flags = aml_get8(mscope->pos++); flags |= aml_get8(mscope->pos++) << 8; break; case 0x02: /* ConnectionField */ mscope->pos++; blen = 0; conn = aml_parse(mscope, 'o', "Connection"); if (conn == NULL) aml_die("Could not parse connection"); conn->node = mscope->node; break; case 0x03: /* ExtendedAccessField */ mscope->pos++; blen = 0; flags = aml_get8(mscope->pos++); flags |= aml_get8(mscope->pos++) << 8; indexval = aml_get8(mscope->pos++); break; default: /* NamedField */ mscope->pos = aml_parsename(mscope->node, mscope->pos, &rv, 1); blen = aml_parselength(mscope); aml_createfield(rv, opcode, data, bpos, blen, conn ? conn : index, indexval, flags); aml_delref(&rv, 0); break; } bpos += blen; } aml_popscope(mscope); } /* * Mutex/Event utility functions */ int acpi_mutex_acquire(struct aml_scope *, struct aml_value *, int); void acpi_mutex_release(struct aml_scope *, struct aml_value *); int acpi_event_wait(struct aml_scope *, struct aml_value *, int); void acpi_event_signal(struct aml_scope *, struct aml_value *); void acpi_event_reset(struct aml_scope *, struct aml_value *); int acpi_mutex_acquire(struct aml_scope *scope, struct aml_value *mtx, int timeout) { if (mtx->v_mtx.owner == NULL || scope == mtx->v_mtx.owner) { /* We are now the owner */ mtx->v_mtx.owner = scope; if (mtx == aml_global_lock) { dnprintf(10,"LOCKING GLOBAL\n"); acpi_glk_enter(); } dnprintf(5,"%s acquires mutex %s\n", scope->node->name, mtx->node->name); return (0); } else if (timeout == 0) { return (-1); } /* Wait for mutex */ return (0); } void acpi_mutex_release(struct aml_scope *scope, struct aml_value *mtx) { if (mtx == aml_global_lock) { dnprintf(10,"UNLOCKING GLOBAL\n"); acpi_glk_leave(); } dnprintf(5, "%s releases mutex %s\n", scope->node->name, mtx->node->name); mtx->v_mtx.owner = NULL; /* Wakeup waiters */ } int acpi_event_wait(struct aml_scope *scope, struct aml_value *evt, int timeout) { /* Wait for event to occur; do work in meantime */ evt->v_evt.state = 0; while (!evt->v_evt.state) { if (!acpi_dotask(acpi_softc) && !cold) tsleep(evt, PWAIT, "acpievt", 1); else delay(100); } if (evt->v_evt.state == 1) { /* Object is signaled */ return (0); } else if (timeout == 0) { /* Zero timeout */ return (-1); } /* Wait for timeout or signal */ return (0); } void acpi_event_signal(struct aml_scope *scope, struct aml_value *evt) { evt->v_evt.state = 1; /* Wakeup waiters */ } void acpi_event_reset(struct aml_scope *scope, struct aml_value *evt) { evt->v_evt.state = 0; } /* Store result value into an object */ void aml_store(struct aml_scope *scope, struct aml_value *lhs , int64_t ival, struct aml_value *rhs) { struct aml_value tmp; struct aml_node *node; int mlen; /* Already set */ if (lhs == rhs || lhs == NULL || lhs->type == AML_OBJTYPE_NOTARGET) { return; } memset(&tmp, 0, sizeof(tmp)); tmp.refcnt=99; if (rhs == NULL) { rhs = _aml_setvalue(&tmp, AML_OBJTYPE_INTEGER, ival, NULL); } if (rhs->type == AML_OBJTYPE_BUFFERFIELD || rhs->type == AML_OBJTYPE_FIELDUNIT) { aml_rwfield(rhs, 0, rhs->v_field.bitlen, &tmp, ACPI_IOREAD); rhs = &tmp; } /* Store to LocalX: free value */ if (lhs->stack >= AMLOP_LOCAL0 && lhs->stack <= AMLOP_LOCAL7) aml_freevalue(lhs); lhs = aml_gettgt(lhs, AMLOP_STORE); switch (lhs->type) { case AML_OBJTYPE_UNINITIALIZED: aml_copyvalue(lhs, rhs); break; case AML_OBJTYPE_BUFFERFIELD: case AML_OBJTYPE_FIELDUNIT: aml_rwfield(lhs, 0, lhs->v_field.bitlen, rhs, ACPI_IOWRITE); break; case AML_OBJTYPE_DEBUGOBJ: break; case AML_OBJTYPE_INTEGER: rhs = aml_convert(rhs, lhs->type, -1); lhs->v_integer = rhs->v_integer; aml_delref(&rhs, "store.int"); break; case AML_OBJTYPE_BUFFER: case AML_OBJTYPE_STRING: rhs = aml_convert(rhs, lhs->type, -1); if (lhs->length < rhs->length) { dnprintf(10, "Overrun! %d,%d\n", lhs->length, rhs->length); aml_freevalue(lhs); _aml_setvalue(lhs, rhs->type, rhs->length, NULL); } mlen = min(lhs->length, rhs->length); memset(lhs->v_buffer, 0x00, lhs->length); memcpy(lhs->v_buffer, rhs->v_buffer, mlen); aml_delref(&rhs, "store.bufstr"); break; case AML_OBJTYPE_PACKAGE: /* Convert to LHS type, copy into LHS */ if (rhs->type != AML_OBJTYPE_PACKAGE) { aml_die("Copy non-package into package?"); } aml_freevalue(lhs); aml_copyvalue(lhs, rhs); break; case AML_OBJTYPE_NAMEREF: node = __aml_searchname(scope->node, lhs->v_nameref, 1); if (node == NULL) { aml_die("Could not create node %s", lhs->v_nameref); } aml_copyvalue(node->value, rhs); break; case AML_OBJTYPE_METHOD: /* Method override */ if (rhs->type != AML_OBJTYPE_INTEGER) { aml_die("Overriding a method with a non-int?"); } aml_freevalue(lhs); aml_copyvalue(lhs, rhs); break; default: aml_die("Store to default type! %x\n", lhs->type); break; } aml_freevalue(&tmp); } #ifdef DDB /* Disassembler routines */ void aml_disprintf(void *arg, const char *fmt, ...); void aml_disprintf(void *arg, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } void aml_disasm(struct aml_scope *scope, int lvl, void (*dbprintf)(void *, const char *, ...) __attribute__((__format__(__kprintf__,2,3))), void *arg) { int pc, opcode; struct aml_opcode *htab; uint64_t ival; struct aml_value *rv, tmp; uint8_t *end = NULL; struct aml_scope ms; char *ch; char mch[64]; if (dbprintf == NULL) dbprintf = aml_disprintf; pc = aml_pc(scope->pos); opcode = aml_parseopcode(scope); htab = aml_findopcode(opcode); /* Display address + indent */ if (lvl <= 0x7FFF) { dbprintf(arg, "%.4x ", pc); for (pc=0; pcpos = aml_parsename(scope->node, scope->pos, &rv, 0); if (rv->type == AML_OBJTYPE_NAMEREF) { ch = "@@@"; aml_delref(&rv, "disasm"); break; } /* if this is a method, get arguments */ strlcpy(mch, aml_nodename(rv->node), sizeof(mch)); if (rv->type == AML_OBJTYPE_METHOD) { strlcat(mch, "(", sizeof(mch)); for (ival=0; ival < AML_METHOD_ARGCOUNT(rv->v_method.flags); ival++) { strlcat(mch, ival ? ", %z" : "%z", sizeof(mch)); } strlcat(mch, ")", sizeof(mch)); } aml_delref(&rv, ""); ch = mch; break; case AMLOP_ZERO: case AMLOP_ONE: case AMLOP_ONES: case AMLOP_LOCAL0: case AMLOP_LOCAL1: case AMLOP_LOCAL2: case AMLOP_LOCAL3: case AMLOP_LOCAL4: case AMLOP_LOCAL5: case AMLOP_LOCAL6: case AMLOP_LOCAL7: case AMLOP_ARG0: case AMLOP_ARG1: case AMLOP_ARG2: case AMLOP_ARG3: case AMLOP_ARG4: case AMLOP_ARG5: case AMLOP_ARG6: case AMLOP_NOP: case AMLOP_REVISION: case AMLOP_DEBUG: case AMLOP_CONTINUE: case AMLOP_BREAKPOINT: case AMLOP_BREAK: ch="%m"; break; case AMLOP_BYTEPREFIX: ch="%b"; break; case AMLOP_WORDPREFIX: ch="%w"; break; case AMLOP_DWORDPREFIX: ch="%d"; break; case AMLOP_QWORDPREFIX: ch="%q"; break; case AMLOP_STRINGPREFIX: ch="%a"; break; case AMLOP_INCREMENT: case AMLOP_DECREMENT: case AMLOP_LNOT: case AMLOP_SIZEOF: case AMLOP_DEREFOF: case AMLOP_REFOF: case AMLOP_OBJECTTYPE: case AMLOP_UNLOAD: case AMLOP_RELEASE: case AMLOP_SIGNAL: case AMLOP_RESET: case AMLOP_STALL: case AMLOP_SLEEP: case AMLOP_RETURN: ch="%m(%n)"; break; case AMLOP_OR: case AMLOP_ADD: case AMLOP_AND: case AMLOP_NAND: case AMLOP_XOR: case AMLOP_SHL: case AMLOP_SHR: case AMLOP_NOR: case AMLOP_MOD: case AMLOP_SUBTRACT: case AMLOP_MULTIPLY: case AMLOP_INDEX: case AMLOP_CONCAT: case AMLOP_CONCATRES: case AMLOP_TOSTRING: ch="%m(%n, %n, %n)"; break; case AMLOP_CREATEBYTEFIELD: case AMLOP_CREATEWORDFIELD: case AMLOP_CREATEDWORDFIELD: case AMLOP_CREATEQWORDFIELD: case AMLOP_CREATEBITFIELD: ch="%m(%n, %n, %N)"; break; case AMLOP_CREATEFIELD: ch="%m(%n, %n, %n, %N)"; break; case AMLOP_DIVIDE: case AMLOP_MID: ch="%m(%n, %n, %n, %n)"; break; case AMLOP_LAND: case AMLOP_LOR: case AMLOP_LNOTEQUAL: case AMLOP_LLESSEQUAL: case AMLOP_LLESS: case AMLOP_LEQUAL: case AMLOP_LGREATEREQUAL: case AMLOP_LGREATER: case AMLOP_NOT: case AMLOP_FINDSETLEFTBIT: case AMLOP_FINDSETRIGHTBIT: case AMLOP_TOINTEGER: case AMLOP_TOBUFFER: case AMLOP_TOHEXSTRING: case AMLOP_TODECSTRING: case AMLOP_FROMBCD: case AMLOP_TOBCD: case AMLOP_WAIT: case AMLOP_LOAD: case AMLOP_STORE: case AMLOP_NOTIFY: case AMLOP_COPYOBJECT: ch="%m(%n, %n)"; break; case AMLOP_ACQUIRE: ch = "%m(%n, %w)"; break; case AMLOP_CONDREFOF: ch="%m(%R, %n)"; break; case AMLOP_ALIAS: ch="%m(%n, %N)"; break; case AMLOP_NAME: ch="%m(%N, %n)"; break; case AMLOP_EVENT: ch="%m(%N)"; break; case AMLOP_MUTEX: ch = "%m(%N, %b)"; break; case AMLOP_OPREGION: ch = "%m(%N, %b, %n, %n)"; break; case AMLOP_DATAREGION: ch="%m(%N, %n, %n, %n)"; break; case AMLOP_FATAL: ch = "%m(%b, %d, %n)"; break; case AMLOP_IF: case AMLOP_WHILE: case AMLOP_SCOPE: case AMLOP_THERMALZONE: case AMLOP_VARPACKAGE: end = aml_parseend(scope); ch = "%m(%n) {\n%T}"; break; case AMLOP_DEVICE: end = aml_parseend(scope); ch = "%m(%N) {\n%T}"; break; case AMLOP_POWERRSRC: end = aml_parseend(scope); ch = "%m(%N, %b, %w) {\n%T}"; break; case AMLOP_PROCESSOR: end = aml_parseend(scope); ch = "%m(%N, %b, %d, %b) {\n%T}"; break; case AMLOP_METHOD: end = aml_parseend(scope); ch = "%m(%N, %b) {\n%T}"; break; case AMLOP_PACKAGE: end = aml_parseend(scope); ch = "%m(%b) {\n%T}"; break; case AMLOP_ELSE: end = aml_parseend(scope); ch = "%m {\n%T}"; break; case AMLOP_BUFFER: end = aml_parseend(scope); ch = "%m(%n) { %B }"; break; case AMLOP_INDEXFIELD: end = aml_parseend(scope); ch = "%m(%n, %n, %b) {\n%F}"; break; case AMLOP_BANKFIELD: end = aml_parseend(scope); ch = "%m(%n, %n, %n, %b) {\n%F}"; break; case AMLOP_FIELD: end = aml_parseend(scope); ch = "%m(%n, %b) {\n%F}"; break; case AMLOP_MATCH: ch = "%m(%n, %b, %n, %b, %n, %n)"; break; case AMLOP_LOADTABLE: ch = "%m(%n, %n, %n, %n, %n, %n)"; break; default: aml_die("opcode = %x\n", opcode); break; } /* Parse printable buffer args */ while (ch && *ch) { char c; if (*ch != '%') { dbprintf(arg,"%c", *(ch++)); continue; } c = *(++ch); switch (c) { case 'b': case 'w': case 'd': case 'q': /* Parse simple object: don't allocate */ aml_parsesimple(scope, c, &tmp); dbprintf(arg,"0x%llx", tmp.v_integer); break; case 'a': dbprintf(arg, "\'%s\'", scope->pos); scope->pos += strlen(scope->pos)+1; break; case 'N': /* Create Name */ rv = aml_parsesimple(scope, c, NULL); dbprintf(arg, "%s", aml_nodename(rv->node)); break; case 'm': /* display mnemonic */ dbprintf(arg, "%s", htab->mnem); break; case 'R': /* Search name */ printf("%s", aml_getname(scope->pos)); scope->pos = aml_parsename(scope->node, scope->pos, &rv, 0); aml_delref(&rv, 0); break; case 'z': case 'n': /* generic arg: recurse */ aml_disasm(scope, lvl | 0x8000, dbprintf, arg); break; case 'B': /* Buffer */ scope->pos = end; break; case 'F': /* Scope: Field List */ memset(&ms, 0, sizeof(ms)); ms.node = scope->node; ms.start = scope->pos; ms.end = end; ms.pos = ms.start; ms.type = AMLOP_FIELD; while (ms.pos < ms.end) { if (*ms.pos == 0x00) { ms.pos++; aml_parselength(&ms); } else if (*ms.pos == 0x01) { ms.pos+=3; } else { ms.pos = aml_parsename(ms.node, ms.pos, &rv, 1); aml_parselength(&ms); dbprintf(arg," %s\n", aml_nodename(rv->node)); aml_delref(&rv, 0); } } /* Display address and closing bracket */ dbprintf(arg,"%.4x ", aml_pc(scope->pos)); for (pc=0; pc<(lvl & 0x7FFF); pc++) { dbprintf(arg," "); } scope->pos = end; break; case 'T': /* Scope: Termlist */ memset(&ms, 0, sizeof(ms)); ms.node = scope->node; ms.start = scope->pos; ms.end = end; ms.pos = ms.start; ms.type = AMLOP_SCOPE; while (ms.pos < ms.end) { aml_disasm(&ms, (lvl + 1) & 0x7FFF, dbprintf, arg); } /* Display address and closing bracket */ dbprintf(arg,"%.4x ", aml_pc(scope->pos)); for (pc=0; pc<(lvl & 0x7FFF); pc++) { dbprintf(arg," "); } scope->pos = end; break; } ch++; } if (lvl <= 0x7FFF) { dbprintf(arg,"\n"); } } #endif /* DDB */ int aml_busy; /* Evaluate method or buffervalue objects */ struct aml_value * aml_eval(struct aml_scope *scope, struct aml_value *my_ret, int ret_type, int argc, struct aml_value *argv) { struct aml_value *tmp = my_ret; struct aml_scope *ms; int idx; switch (tmp->type) { case AML_OBJTYPE_NAMEREF: my_ret = aml_seterror(scope, "Undefined name: %s", aml_getname(my_ret->v_nameref)); break; case AML_OBJTYPE_METHOD: dnprintf(10,"\n--== Eval Method [%s, %d args] to %c ==--\n", aml_nodename(tmp->node), AML_METHOD_ARGCOUNT(tmp->v_method.flags), ret_type); ms = aml_pushscope(scope, tmp, tmp->node, AMLOP_METHOD); /* Parse method arguments */ for (idx=0; idxv_method.flags); idx++) { struct aml_value *sp; sp = aml_getstack(ms, AMLOP_ARG0+idx); if (argv) { aml_copyvalue(sp, &argv[idx]); } else { _aml_setvalue(sp, AML_OBJTYPE_OBJREF, AMLOP_ARG0 + idx, 0); sp->v_objref.ref = aml_parse(scope, 't', "ARGX"); } } #ifdef ACPI_DEBUG aml_showstack(ms); #endif /* Evaluate method scope */ aml_root.start = tmp->v_method.base; if (tmp->v_method.fneval != NULL) { my_ret = tmp->v_method.fneval(ms, NULL); } else { aml_parse(ms, 'T', "METHEVAL"); my_ret = ms->retv; } dnprintf(10,"\n--==Finished evaluating method: %s %c\n", aml_nodename(tmp->node), ret_type); #ifdef ACPI_DEBUG aml_showvalue(my_ret); aml_showstack(ms); #endif aml_popscope(ms); break; case AML_OBJTYPE_BUFFERFIELD: case AML_OBJTYPE_FIELDUNIT: my_ret = aml_allocvalue(0,0,NULL); dnprintf(20,"quick: Convert Bufferfield to %c %p\n", ret_type, my_ret); aml_rwfield(tmp, 0, tmp->v_field.bitlen, my_ret, ACPI_IOREAD); break; } if (ret_type == 'i' && my_ret && my_ret->type != AML_OBJTYPE_INTEGER) { #ifndef SMALL_KERNEL aml_showvalue(my_ret); #endif aml_die("Not Integer"); } return my_ret; } /* * The following opcodes produce return values * TOSTRING -> Str * TOHEXSTR -> Str * TODECSTR -> Str * STRINGPFX -> Str * BUFFER -> Buf * CONCATRES -> Buf * TOBUFFER -> Buf * MID -> Buf|Str * CONCAT -> Buf|Str * PACKAGE -> Pkg * VARPACKAGE -> Pkg * LOCALx -> Obj * ARGx -> Obj * NAMECHAR -> Obj * REFOF -> ObjRef * INDEX -> ObjRef * DEREFOF -> DataRefObj * COPYOBJECT -> DataRefObj * STORE -> DataRefObj * ZERO -> Int * ONE -> Int * ONES -> Int * REVISION -> Int * B/W/D/Q -> Int * OR -> Int * AND -> Int * ADD -> Int * NAND -> Int * XOR -> Int * SHL -> Int * SHR -> Int * NOR -> Int * MOD -> Int * SUBTRACT -> Int * MULTIPLY -> Int * DIVIDE -> Int * NOT -> Int * TOBCD -> Int * FROMBCD -> Int * FSLEFTBIT -> Int * FSRIGHTBIT -> Int * INCREMENT -> Int * DECREMENT -> Int * TOINTEGER -> Int * MATCH -> Int * SIZEOF -> Int * OBJECTTYPE -> Int * TIMER -> Int * CONDREFOF -> Bool * ACQUIRE -> Bool * WAIT -> Bool * LNOT -> Bool * LAND -> Bool * LOR -> Bool * LLESS -> Bool * LEQUAL -> Bool * LGREATER -> Bool * LNOTEQUAL -> Bool * LLESSEQUAL -> Bool * LGREATEREQ -> Bool * LOADTABLE -> DDB * DEBUG -> Debug * The following opcodes do not generate a return value: * NOP * BREAKPOINT * RELEASE * RESET * SIGNAL * NAME * ALIAS * OPREGION * DATAREGION * EVENT * MUTEX * SCOPE * DEVICE * THERMALZONE * POWERRSRC * PROCESSOR * METHOD * CREATEFIELD * CREATEBITFIELD * CREATEBYTEFIELD * CREATEWORDFIELD * CREATEDWORDFIELD * CREATEQWORDFIELD * FIELD * INDEXFIELD * BANKFIELD * STALL * SLEEP * NOTIFY * FATAL * LOAD * UNLOAD * IF * ELSE * WHILE * BREAK * CONTINUE */ /* Parse a simple object from AML Bytestream */ struct aml_value * aml_parsesimple(struct aml_scope *scope, char ch, struct aml_value *rv) { if (rv == NULL) rv = aml_allocvalue(0,0,NULL); switch (ch) { case AML_ARG_REVISION: _aml_setvalue(rv, AML_OBJTYPE_INTEGER, AML_REVISION, NULL); break; case AML_ARG_DEBUG: _aml_setvalue(rv, AML_OBJTYPE_DEBUGOBJ, 0, NULL); break; case AML_ARG_BYTE: _aml_setvalue(rv, AML_OBJTYPE_INTEGER, aml_get8(scope->pos), NULL); scope->pos += 1; break; case AML_ARG_WORD: _aml_setvalue(rv, AML_OBJTYPE_INTEGER, aml_get16(scope->pos), NULL); scope->pos += 2; break; case AML_ARG_DWORD: _aml_setvalue(rv, AML_OBJTYPE_INTEGER, aml_get32(scope->pos), NULL); scope->pos += 4; break; case AML_ARG_QWORD: _aml_setvalue(rv, AML_OBJTYPE_INTEGER, aml_get64(scope->pos), NULL); scope->pos += 8; break; case AML_ARG_STRING: _aml_setvalue(rv, AML_OBJTYPE_STRING, -1, scope->pos); scope->pos += rv->length+1; break; } return rv; } /* * Main Opcode Parser/Evaluator * * ret_type is expected type for return value * 'o' = Data Object (Int/Str/Buf/Pkg/Name) * 'i' = Integer * 't' = TermArg (Int/Str/Buf/Pkg) * 'r' = Target (NamedObj/Local/Arg/Null) * 'S' = SuperName (NamedObj/Local/Arg) * 'T' = TermList */ #define aml_debugger(x) int maxdp; struct aml_value * aml_gettgt(struct aml_value *val, int opcode) { while (val && val->type == AML_OBJTYPE_OBJREF) { val = val->v_objref.ref; } return val; } struct aml_value * aml_seterror(struct aml_scope *scope, const char *fmt, ...) { va_list ap; va_start(ap, fmt); printf("### AML PARSE ERROR (0x%x): ", aml_pc(scope->pos)); vprintf(fmt, ap); printf("\n"); va_end(ap); while (scope) { scope->pos = scope->end; scope = scope->parent; } aml_error++; return aml_allocvalue(AML_OBJTYPE_INTEGER, 0, 0); } struct aml_value * aml_loadtable(struct acpi_softc *sc, const char *signature, const char *oemid, const char *oemtableid, const char *rootpath, const char *parameterpath, struct aml_value *parameterdata) { struct acpi_table_header *hdr; struct acpi_dsdt *p_dsdt; struct acpi_q *entry; if (strlen(rootpath) > 0) aml_die("LoadTable: RootPathString unsupported"); if (strlen(parameterpath) > 0) aml_die("LoadTable: ParameterPathString unsupported"); SIMPLEQ_FOREACH(entry, &sc->sc_tables, q_next) { hdr = entry->q_table; if (strncmp(hdr->signature, signature, sizeof(hdr->signature)) == 0 && strncmp(hdr->oemid, oemid, sizeof(hdr->oemid)) == 0 && strncmp(hdr->oemtableid, oemtableid, sizeof(hdr->oemtableid)) == 0) { p_dsdt = entry->q_table; acpi_parse_aml(sc, p_dsdt->aml, p_dsdt->hdr_length - sizeof(p_dsdt->hdr)); return aml_allocvalue(AML_OBJTYPE_DDBHANDLE, 0, 0); } } return aml_allocvalue(AML_OBJTYPE_INTEGER, 0, 0); } /* Load new SSDT scope from memory address */ struct aml_scope * aml_load(struct acpi_softc *sc, struct aml_scope *scope, struct aml_value *rgn, struct aml_value *ddb) { struct acpi_q *entry; struct acpi_dsdt *p_ssdt; struct aml_value tmp; ddb->type = AML_OBJTYPE_DDBHANDLE; ddb->v_integer = 0; memset(&tmp, 0, sizeof(tmp)); if (rgn->type != AML_OBJTYPE_OPREGION || rgn->v_opregion.iospace != GAS_SYSTEM_MEMORY) goto fail; /* Load SSDT from memory */ entry = acpi_maptable(sc, rgn->v_opregion.iobase, "SSDT", NULL, NULL, 1); if (entry == NULL) goto fail; dnprintf(10, "%s: loaded SSDT %s @ %llx\n", sc->sc_dev.dv_xname, aml_nodename(rgn->node), rgn->v_opregion.iobase); ddb->v_integer = entry->q_id; p_ssdt = entry->q_table; tmp.v_buffer = p_ssdt->aml; tmp.length = p_ssdt->hdr_length - sizeof(p_ssdt->hdr); return aml_pushscope(scope, &tmp, scope->node, AMLOP_LOAD); fail: printf("%s: unable to load %s\n", sc->sc_dev.dv_xname, aml_nodename(rgn->node)); return NULL; } struct aml_value * aml_parse(struct aml_scope *scope, int ret_type, const char *stype) { int opcode, idx, pc; struct aml_opcode *htab; struct aml_value *opargs[8], *my_ret, *rv; struct aml_scope *mscope, *iscope; uint8_t *start, *end; const char *ch; int64_t ival; struct timespec ts; my_ret = NULL; if (scope == NULL || scope->pos >= scope->end) { return NULL; } if (odp++ > 125) panic("depth"); if (odp > maxdp) { maxdp = odp; dnprintf(10, "max depth: %d\n", maxdp); } end = NULL; iscope = scope; start: /* --== Stage 0: Get Opcode ==-- */ start = scope->pos; pc = aml_pc(scope->pos); aml_debugger(scope); opcode = aml_parseopcode(scope); htab = aml_findopcode(opcode); if (htab == NULL) { /* No opcode handler */ aml_die("Unknown opcode: %.4x @ %.4x", opcode, pc); } dnprintf(18,"%.4x %s\n", pc, aml_mnem(opcode, scope->pos)); /* --== Stage 1: Process opcode arguments ==-- */ memset(opargs, 0, sizeof(opargs)); idx = 0; for (ch = htab->args; *ch; ch++) { rv = NULL; switch (*ch) { case AML_ARG_OBJLEN: end = aml_parseend(scope); break; case AML_ARG_IFELSE: /* Special Case: IF-ELSE:piTbpT or IF:piT */ ch = (*end == AMLOP_ELSE && end < scope->end) ? "-TbpT" : "-T"; break; /* Complex arguments */ case 's': case 'S': case AML_ARG_TARGET: case AML_ARG_TERMOBJ: case AML_ARG_INTEGER: if (*ch == 'r' && *scope->pos == AMLOP_ZERO) { /* Special case: NULL Target */ rv = aml_allocvalue(AML_OBJTYPE_NOTARGET, 0, NULL); scope->pos++; } else { rv = aml_parse(scope, *ch, htab->mnem); if (rv == NULL || aml_error) goto parse_error; } break; /* Simple arguments */ case AML_ARG_BUFFER: case AML_ARG_METHOD: case AML_ARG_FIELDLIST: case AML_ARG_TERMOBJLIST: rv = aml_allocvalue(AML_OBJTYPE_SCOPE, 0, NULL); rv->v_buffer = scope->pos; rv->length = end - scope->pos; scope->pos = end; break; case AML_ARG_CONST: rv = aml_allocvalue(AML_OBJTYPE_INTEGER, (char)opcode, NULL); break; case AML_ARG_CREATENAME: scope->pos = aml_parsename(scope->node, scope->pos, &rv, 1); break; case AML_ARG_SEARCHNAME: scope->pos = aml_parsename(scope->node, scope->pos, &rv, 0); break; case AML_ARG_BYTE: case AML_ARG_WORD: case AML_ARG_DWORD: case AML_ARG_QWORD: case AML_ARG_DEBUG: case AML_ARG_STRING: case AML_ARG_REVISION: rv = aml_parsesimple(scope, *ch, NULL); break; case AML_ARG_STKLOCAL: case AML_ARG_STKARG: rv = aml_getstack(scope, opcode); break; default: aml_die("Unknown arg type: %c\n", *ch); break; } if (rv != NULL) opargs[idx++] = rv; } /* --== Stage 2: Process opcode ==-- */ ival = 0; my_ret = NULL; mscope = NULL; switch (opcode) { case AMLOP_NOP: case AMLOP_BREAKPOINT: break; case AMLOP_LOCAL0: case AMLOP_LOCAL1: case AMLOP_LOCAL2: case AMLOP_LOCAL3: case AMLOP_LOCAL4: case AMLOP_LOCAL5: case AMLOP_LOCAL6: case AMLOP_LOCAL7: case AMLOP_ARG0: case AMLOP_ARG1: case AMLOP_ARG2: case AMLOP_ARG3: case AMLOP_ARG4: case AMLOP_ARG5: case AMLOP_ARG6: my_ret = opargs[0]; aml_addref(my_ret, htab->mnem); break; case AMLOP_NAMECHAR: /* opargs[0] = named object (node != NULL), or nameref */ my_ret = opargs[0]; if (scope->type == AMLOP_PACKAGE) { /* Special case for package */ if (my_ret->type == AML_OBJTYPE_NAMEREF) my_ret = aml_allocvalue(AML_OBJTYPE_STRING, -1, aml_getname(my_ret->v_nameref)); else if (my_ret->node) my_ret = aml_allocvalue(AML_OBJTYPE_STRING, -1, aml_nodename(my_ret->node)); break; } if (my_ret->type == AML_OBJTYPE_OBJREF) { my_ret = my_ret->v_objref.ref; aml_addref(my_ret, "de-alias"); } if (ret_type == 'i' || ret_type == 't' || ret_type == 'T') { /* Return TermArg or Integer: Evaluate object */ my_ret = aml_eval(scope, my_ret, ret_type, 0, NULL); } else if (my_ret->type == AML_OBJTYPE_METHOD) { /* This should only happen with CondRef */ dnprintf(12,"non-termarg method : %s\n", stype); aml_addref(my_ret, "zoom"); } break; case AMLOP_ZERO: case AMLOP_ONE: case AMLOP_ONES: case AMLOP_DEBUG: case AMLOP_REVISION: case AMLOP_BYTEPREFIX: case AMLOP_WORDPREFIX: case AMLOP_DWORDPREFIX: case AMLOP_QWORDPREFIX: case AMLOP_STRINGPREFIX: my_ret = opargs[0]; break; case AMLOP_BUFFER: /* Buffer: iB => Buffer */ my_ret = aml_allocvalue(AML_OBJTYPE_BUFFER, opargs[0]->v_integer, NULL); memcpy(my_ret->v_buffer, opargs[1]->v_buffer, opargs[1]->length); break; case AMLOP_PACKAGE: case AMLOP_VARPACKAGE: /* Package/VarPackage: bT/iT => Package */ my_ret = aml_allocvalue(AML_OBJTYPE_PACKAGE, opargs[0]->v_integer, 0); mscope = aml_pushscope(scope, opargs[1], scope->node, AMLOP_PACKAGE); /* Recursively parse package contents */ for (idx=0; idxlength; idx++) { rv = aml_parse(mscope, 'o', "Package"); if (rv != NULL) { aml_delref(&my_ret->v_package[idx], "pkginit"); my_ret->v_package[idx] = rv; } } aml_popscope(mscope); mscope = NULL; break; /* Math/Logical operations */ case AMLOP_OR: case AMLOP_ADD: case AMLOP_AND: case AMLOP_NAND: case AMLOP_XOR: case AMLOP_SHL: case AMLOP_SHR: case AMLOP_NOR: case AMLOP_MOD: case AMLOP_SUBTRACT: case AMLOP_MULTIPLY: /* XXX: iir => I */ ival = aml_evalexpr(opargs[0]->v_integer, opargs[1]->v_integer, opcode); aml_store(scope, opargs[2], ival, NULL); break; case AMLOP_DIVIDE: /* Divide: iirr => I */ if (opargs[1]->v_integer == 0) { my_ret = aml_seterror(scope, "Divide by Zero!"); break; } ival = aml_evalexpr(opargs[0]->v_integer, opargs[1]->v_integer, AMLOP_MOD); aml_store(scope, opargs[2], ival, NULL); ival = aml_evalexpr(opargs[0]->v_integer, opargs[1]->v_integer, AMLOP_DIVIDE); aml_store(scope, opargs[3], ival, NULL); break; case AMLOP_NOT: case AMLOP_TOBCD: case AMLOP_FROMBCD: case AMLOP_FINDSETLEFTBIT: case AMLOP_FINDSETRIGHTBIT: /* XXX: ir => I */ ival = aml_evalexpr(opargs[0]->v_integer, 0, opcode); aml_store(scope, opargs[1], ival, NULL); break; case AMLOP_INCREMENT: case AMLOP_DECREMENT: /* Inc/Dec: S => I */ my_ret = aml_eval(scope, opargs[0], AML_ARG_INTEGER, 0, NULL); ival = aml_evalexpr(my_ret->v_integer, 1, opcode); aml_store(scope, opargs[0], ival, NULL); break; case AMLOP_LNOT: /* LNot: i => Bool */ ival = aml_evalexpr(opargs[0]->v_integer, 0, opcode); break; case AMLOP_LOR: case AMLOP_LAND: /* XXX: ii => Bool */ ival = aml_evalexpr(opargs[0]->v_integer, opargs[1]->v_integer, opcode); break; case AMLOP_LLESS: case AMLOP_LEQUAL: case AMLOP_LGREATER: case AMLOP_LNOTEQUAL: case AMLOP_LLESSEQUAL: case AMLOP_LGREATEREQUAL: /* XXX: tt => Bool */ ival = aml_compare(opargs[0], opargs[1], opcode); break; /* Reference/Store operations */ case AMLOP_CONDREFOF: /* CondRef: rr => I */ ival = 0; if (opargs[0]->node != NULL) { /* Create Object Reference */ rv = aml_allocvalue(AML_OBJTYPE_OBJREF, opcode, opargs[0]); aml_addref(opargs[0], "CondRef"); aml_store(scope, opargs[1], 0, rv); aml_delref(&rv, 0); /* Mark that we found it */ ival = -1; } break; case AMLOP_REFOF: /* RefOf: r => ObjRef */ my_ret = aml_allocvalue(AML_OBJTYPE_OBJREF, opcode, opargs[0]); aml_addref(my_ret->v_objref.ref, "RefOf"); break; case AMLOP_INDEX: /* Index: tir => ObjRef */ idx = opargs[1]->v_integer; if (idx >= opargs[0]->length || idx < 0) { #ifndef SMALL_KERNEL aml_showvalue(opargs[0]); #endif aml_die("Index out of bounds %d/%d\n", idx, opargs[0]->length); } switch (opargs[0]->type) { case AML_OBJTYPE_PACKAGE: /* Don't set opargs[0] to NULL */ if (ret_type == 't' || ret_type == 'i' || ret_type == 'T') { my_ret = opargs[0]->v_package[idx]; aml_addref(my_ret, "Index.Package"); } else { my_ret = aml_allocvalue(AML_OBJTYPE_OBJREF, AMLOP_PACKAGE, opargs[0]->v_package[idx]); aml_addref(my_ret->v_objref.ref, "Index.Package"); } break; case AML_OBJTYPE_BUFFER: case AML_OBJTYPE_STRING: case AML_OBJTYPE_INTEGER: rv = aml_convert(opargs[0], AML_OBJTYPE_BUFFER, -1); if (ret_type == 't' || ret_type == 'i' || ret_type == 'T') { dnprintf(12,"Index.Buf Term: %d = %x\n", idx, rv->v_buffer[idx]); ival = rv->v_buffer[idx]; } else { dnprintf(12, "Index.Buf Targ\n"); my_ret = aml_allocvalue(0,0,NULL); aml_createfield(my_ret, AMLOP_INDEX, rv, 8 * idx, 8, NULL, 0, AML_FIELD_BYTEACC); } aml_delref(&rv, "Index.BufStr"); break; default: aml_die("Unknown index : %x\n", opargs[0]->type); break; } aml_store(scope, opargs[2], ival, my_ret); break; case AMLOP_DEREFOF: /* DerefOf: t:ObjRef => DataRefObj */ if (opargs[0]->type == AML_OBJTYPE_OBJREF) { my_ret = opargs[0]->v_objref.ref; aml_addref(my_ret, "DerefOf"); } else { my_ret = opargs[0]; //aml_addref(my_ret, "DerefOf"); } break; case AMLOP_COPYOBJECT: /* CopyObject: t:DataRefObj, s:implename => DataRefObj */ my_ret = opargs[0]; aml_freevalue(opargs[1]); aml_copyvalue(opargs[1], opargs[0]); break; case AMLOP_STORE: /* Store: t:DataRefObj, S:upername => DataRefObj */ my_ret = opargs[0]; aml_store(scope, opargs[1], 0, opargs[0]); break; /* Conversion */ case AMLOP_TOINTEGER: /* Source:CData, Result => Integer */ my_ret = aml_convert(opargs[0], AML_OBJTYPE_INTEGER, -1); aml_store(scope, opargs[1], 0, my_ret); break; case AMLOP_TOBUFFER: /* Source:CData, Result => Buffer */ my_ret = aml_convert(opargs[0], AML_OBJTYPE_BUFFER, -1); aml_store(scope, opargs[1], 0, my_ret); break; case AMLOP_TOHEXSTRING: /* Source:CData, Result => String */ my_ret = aml_convert(opargs[0], AML_OBJTYPE_HEXSTRING, -1); aml_store(scope, opargs[1], 0, my_ret); break; case AMLOP_TODECSTRING: /* Source:CData, Result => String */ my_ret = aml_convert(opargs[0], AML_OBJTYPE_DECSTRING, -1); aml_store(scope, opargs[1], 0, my_ret); break; case AMLOP_TOSTRING: /* Source:B, Length:I, Result => String */ my_ret = aml_convert(opargs[0], AML_OBJTYPE_STRING, opargs[1]->v_integer); aml_store(scope, opargs[2], 0, my_ret); break; case AMLOP_CONCAT: /* Source1:CData, Source2:CData, Result => CData */ my_ret = aml_concat(opargs[0], opargs[1]); aml_store(scope, opargs[2], 0, my_ret); break; case AMLOP_CONCATRES: /* Concat two resource buffers: buf1, buf2, result => Buffer */ my_ret = aml_concatres(opargs[0], opargs[1]); aml_store(scope, opargs[2], 0, my_ret); break; case AMLOP_MID: /* Source:BS, Index:I, Length:I, Result => BS */ my_ret = aml_mid(opargs[0], opargs[1]->v_integer, opargs[2]->v_integer); aml_store(scope, opargs[3], 0, my_ret); break; case AMLOP_MATCH: /* Match: Pkg, Op1, Val1, Op2, Val2, Index */ ival = aml_match(opargs[0], opargs[5]->v_integer, opargs[1]->v_integer, opargs[2]->v_integer, opargs[3]->v_integer, opargs[4]->v_integer); break; case AMLOP_SIZEOF: /* Sizeof: S => i */ rv = aml_gettgt(opargs[0], opcode); ival = rv->length; break; case AMLOP_OBJECTTYPE: /* ObjectType: S => i */ rv = aml_gettgt(opargs[0], opcode); ival = rv->type; break; /* Mutex/Event handlers */ case AMLOP_ACQUIRE: /* Acquire: Sw => Bool */ rv = aml_gettgt(opargs[0], opcode); ival = acpi_mutex_acquire(scope, rv, opargs[1]->v_integer); break; case AMLOP_RELEASE: /* Release: S */ rv = aml_gettgt(opargs[0], opcode); acpi_mutex_release(scope, rv); break; case AMLOP_WAIT: /* Wait: Si => Bool */ rv = aml_gettgt(opargs[0], opcode); ival = acpi_event_wait(scope, rv, opargs[1]->v_integer); break; case AMLOP_RESET: /* Reset: S */ rv = aml_gettgt(opargs[0], opcode); acpi_event_reset(scope, rv); break; case AMLOP_SIGNAL: /* Signal: S */ rv = aml_gettgt(opargs[0], opcode); acpi_event_signal(scope, rv); break; /* Named objects */ case AMLOP_NAME: /* Name: Nt */ rv = opargs[0]; aml_freevalue(rv); aml_copyvalue(rv, opargs[1]); break; case AMLOP_ALIAS: /* Alias: nN */ rv = _aml_setvalue(opargs[1], AML_OBJTYPE_OBJREF, opcode, 0); rv->v_objref.ref = aml_gettgt(opargs[0], opcode); aml_addref(rv->v_objref.ref, "Alias"); break; case AMLOP_OPREGION: /* OpRegion: Nbii */ rv = _aml_setvalue(opargs[0], AML_OBJTYPE_OPREGION, 0, 0); rv->v_opregion.iospace = opargs[1]->v_integer; rv->v_opregion.iobase = opargs[2]->v_integer; rv->v_opregion.iolen = opargs[3]->v_integer; rv->v_opregion.flag = 0; break; case AMLOP_DATAREGION: /* DataTableRegion: N,t:SigStr,t:OemIDStr,t:OemTableIDStr */ rv = _aml_setvalue(opargs[0], AML_OBJTYPE_OPREGION, 0, 0); rv->v_opregion.iospace = GAS_SYSTEM_MEMORY; rv->v_opregion.iobase = 0; rv->v_opregion.iolen = 0; aml_die("AML-DataTableRegion\n"); break; case AMLOP_EVENT: /* Event: N */ rv = _aml_setvalue(opargs[0], AML_OBJTYPE_EVENT, 0, 0); rv->v_integer = 0; break; case AMLOP_MUTEX: /* Mutex: Nw */ rv = _aml_setvalue(opargs[0], AML_OBJTYPE_MUTEX, 0, 0); rv->v_mtx.synclvl = opargs[1]->v_integer; break; case AMLOP_SCOPE: /* Scope: NT */ rv = opargs[0]; if (rv->type == AML_OBJTYPE_NAMEREF) { printf("Undefined scope: %s\n", aml_getname(rv->v_nameref)); break; } mscope = aml_pushscope(scope, opargs[1], rv->node, opcode); break; case AMLOP_DEVICE: /* Device: NT */ rv = _aml_setvalue(opargs[0], AML_OBJTYPE_DEVICE, 0, 0); mscope = aml_pushscope(scope, opargs[1], rv->node, opcode); break; case AMLOP_THERMALZONE: /* ThermalZone: NT */ rv = _aml_setvalue(opargs[0], AML_OBJTYPE_THERMZONE, 0, 0); mscope = aml_pushscope(scope, opargs[1], rv->node, opcode); break; case AMLOP_POWERRSRC: /* PowerRsrc: NbwT */ rv = _aml_setvalue(opargs[0], AML_OBJTYPE_POWERRSRC, 0, 0); rv->v_powerrsrc.pwr_level = opargs[1]->v_integer; rv->v_powerrsrc.pwr_order = opargs[2]->v_integer; mscope = aml_pushscope(scope, opargs[3], rv->node, opcode); break; case AMLOP_PROCESSOR: /* Processor: NbdbT */ rv = _aml_setvalue(opargs[0], AML_OBJTYPE_PROCESSOR, 0, 0); rv->v_processor.proc_id = opargs[1]->v_integer; rv->v_processor.proc_addr = opargs[2]->v_integer; rv->v_processor.proc_len = opargs[3]->v_integer; mscope = aml_pushscope(scope, opargs[4], rv->node, opcode); break; case AMLOP_METHOD: /* Method: NbM */ rv = _aml_setvalue(opargs[0], AML_OBJTYPE_METHOD, 0, 0); rv->v_method.flags = opargs[1]->v_integer; rv->v_method.start = opargs[2]->v_buffer; rv->v_method.end = rv->v_method.start + opargs[2]->length; rv->v_method.base = aml_root.start; break; /* Field objects */ case AMLOP_CREATEFIELD: /* Source:B, BitIndex:I, NumBits:I, FieldName */ rv = _aml_setvalue(opargs[3], AML_OBJTYPE_BUFFERFIELD, 0, 0); aml_createfield(rv, opcode, opargs[0], opargs[1]->v_integer, opargs[2]->v_integer, NULL, 0, 0); break; case AMLOP_CREATEBITFIELD: /* Source:B, BitIndex:I, FieldName */ rv = _aml_setvalue(opargs[2], AML_OBJTYPE_BUFFERFIELD, 0, 0); aml_createfield(rv, opcode, opargs[0], opargs[1]->v_integer, 1, NULL, 0, 0); break; case AMLOP_CREATEBYTEFIELD: /* Source:B, ByteIndex:I, FieldName */ rv = _aml_setvalue(opargs[2], AML_OBJTYPE_BUFFERFIELD, 0, 0); aml_createfield(rv, opcode, opargs[0], opargs[1]->v_integer*8, 8, NULL, 0, AML_FIELD_BYTEACC); break; case AMLOP_CREATEWORDFIELD: /* Source:B, ByteIndex:I, FieldName */ rv = _aml_setvalue(opargs[2], AML_OBJTYPE_BUFFERFIELD, 0, 0); aml_createfield(rv, opcode, opargs[0], opargs[1]->v_integer*8, 16, NULL, 0, AML_FIELD_WORDACC); break; case AMLOP_CREATEDWORDFIELD: /* Source:B, ByteIndex:I, FieldName */ rv = _aml_setvalue(opargs[2], AML_OBJTYPE_BUFFERFIELD, 0, 0); aml_createfield(rv, opcode, opargs[0], opargs[1]->v_integer*8, 32, NULL, 0, AML_FIELD_DWORDACC); break; case AMLOP_CREATEQWORDFIELD: /* Source:B, ByteIndex:I, FieldName */ rv = _aml_setvalue(opargs[2], AML_OBJTYPE_BUFFERFIELD, 0, 0); aml_createfield(rv, opcode, opargs[0], opargs[1]->v_integer*8, 64, NULL, 0, AML_FIELD_QWORDACC); break; case AMLOP_FIELD: /* Field: n:OpRegion, b:Flags, F:ieldlist */ mscope = aml_pushscope(scope, opargs[2], scope->node, opcode); aml_parsefieldlist(mscope, opcode, opargs[1]->v_integer, opargs[0], NULL, 0); mscope = NULL; break; case AMLOP_INDEXFIELD: /* IndexField: n:Index, n:Data, b:Flags, F:ieldlist */ mscope = aml_pushscope(scope, opargs[3], scope->node, opcode); aml_parsefieldlist(mscope, opcode, opargs[2]->v_integer, opargs[1], opargs[0], 0); mscope = NULL; break; case AMLOP_BANKFIELD: /* BankField: n:OpRegion, n:Field, i:Bank, b:Flags, F:ieldlist */ mscope = aml_pushscope(scope, opargs[4], scope->node, opcode); aml_parsefieldlist(mscope, opcode, opargs[3]->v_integer, opargs[0], opargs[1], opargs[2]->v_integer); mscope = NULL; break; /* Misc functions */ case AMLOP_STALL: /* Stall: i */ acpi_stall(opargs[0]->v_integer); break; case AMLOP_SLEEP: /* Sleep: i */ acpi_sleep(opargs[0]->v_integer, "amlsleep"); break; case AMLOP_NOTIFY: /* Notify: Si */ rv = aml_gettgt(opargs[0], opcode); dnprintf(50,"Notifying: %s %llx\n", aml_nodename(rv->node), opargs[1]->v_integer); aml_notify(rv->node, opargs[1]->v_integer); break; case AMLOP_TIMER: /* Timer: => i */ nanouptime(&ts); ival = ts.tv_sec * 10000000 + ts.tv_nsec / 100; break; case AMLOP_FATAL: /* Fatal: bdi */ aml_die("AML FATAL ERROR: %x,%x,%x\n", opargs[0]->v_integer, opargs[1]->v_integer, opargs[2]->v_integer); break; case AMLOP_LOADTABLE: /* LoadTable(Sig:Str, OEMID:Str, OEMTable:Str, [RootPath:Str], [ParmPath:Str], [ParmData:DataRefObj]) => DDBHandle */ my_ret = aml_loadtable(acpi_softc, opargs[0]->v_string, opargs[1]->v_string, opargs[2]->v_string, opargs[3]->v_string, opargs[4]->v_string, opargs[5]); break; case AMLOP_LOAD: /* Load(Object:NameString, DDBHandle:SuperName) */ mscope = aml_load(acpi_softc, scope, opargs[0], opargs[1]); break; case AMLOP_UNLOAD: /* DDBHandle */ aml_die("Unload"); break; /* Control Flow */ case AMLOP_IF: /* Arguments: iT or iTbT */ if (opargs[0]->v_integer) { dnprintf(10,"parse-if @ %.4x\n", pc); mscope = aml_pushscope(scope, opargs[1], scope->node, AMLOP_IF); } else if (opargs[3] != NULL) { dnprintf(10,"parse-else @ %.4x\n", pc); mscope = aml_pushscope(scope, opargs[3], scope->node, AMLOP_ELSE); } break; case AMLOP_WHILE: if (opargs[0]->v_integer) { /* Set parent position to start of WHILE */ scope->pos = start; mscope = aml_pushscope(scope, opargs[1], scope->node, AMLOP_WHILE); } break; case AMLOP_BREAK: /* Break: Find While Scope parent, mark type as null */ aml_findscope(scope, AMLOP_WHILE, AMLOP_BREAK); break; case AMLOP_CONTINUE: /* Find Scope.. mark all objects as invalid on way to root */ aml_findscope(scope, AMLOP_WHILE, AMLOP_CONTINUE); break; case AMLOP_RETURN: mscope = aml_findscope(scope, AMLOP_METHOD, AMLOP_RETURN); if (mscope->retv) { aml_die("already allocated\n"); } mscope->retv = aml_allocvalue(0,0,NULL); aml_copyvalue(mscope->retv, opargs[0]); mscope = NULL; break; default: /* may be set direct result */ aml_die("Unknown opcode: %x:%s\n", opcode, htab->mnem); break; } if (mscope != NULL) { /* Change our scope to new scope */ scope = mscope; } if ((ret_type == 'i' || ret_type == 't') && my_ret == NULL) { dnprintf(10,"quick: %.4x [%s] alloc return integer = 0x%llx\n", pc, htab->mnem, ival); my_ret = aml_allocvalue(AML_OBJTYPE_INTEGER, ival, NULL); } if (ret_type == 'i' && my_ret && my_ret->type != AML_OBJTYPE_INTEGER) { dnprintf(10,"quick: %.4x convert to integer %s -> %s\n", pc, htab->mnem, stype); my_ret = aml_convert(my_ret, AML_OBJTYPE_INTEGER, -1); } if (my_ret != NULL) { /* Display result */ dnprintf(20,"quick: %.4x %18s %c %.4x\n", pc, stype, ret_type, my_ret->stack); } /* End opcode: display/free arguments */ parse_error: for (idx=0; idx<8; idx++) { if (opargs[idx] == my_ret) opargs[idx] = NULL; aml_delref(&opargs[idx], "oparg"); } /* If parsing whole scope and not done, start again */ if (ret_type == 'T') { aml_delref(&my_ret, "scope.loop"); while (scope->pos >= scope->end && scope != iscope) { /* Pop intermediate scope */ scope = aml_popscope(scope); } if (scope->pos && scope->pos < scope->end) goto start; } odp--; dnprintf(50, ">>return [%s] %s %c %p\n", aml_nodename(scope->node), stype, ret_type, my_ret); return my_ret; } int acpi_parse_aml(struct acpi_softc *sc, uint8_t *start, uint32_t length) { struct aml_scope *scope; struct aml_value res; aml_root.start = start; memset(&res, 0, sizeof(res)); res.type = AML_OBJTYPE_SCOPE; res.length = length; res.v_buffer = start; /* Push toplevel scope, parse AML */ aml_error = 0; scope = aml_pushscope(NULL, &res, &aml_root, AMLOP_SCOPE); aml_busy++; aml_parse(scope, 'T', "TopLevel"); aml_busy--; aml_popscope(scope); if (aml_error) { printf("error in acpi_parse_aml\n"); return -1; } return (0); } /* * @@@: External API * * evaluate an AML node * Returns a copy of the value in res (must be freed by user) */ int aml_evalnode(struct acpi_softc *sc, struct aml_node *node, int argc, struct aml_value *argv, struct aml_value *res) { struct aml_value *xres; if (res) memset(res, 0, sizeof(*res)); if (node == NULL || node->value == NULL) return (ACPI_E_BADVALUE); dnprintf(12,"EVALNODE: %s %lx\n", aml_nodename(node), acpi_nalloc); aml_error = 0; xres = aml_eval(NULL, node->value, 't', argc, argv); if (xres) { if (res) aml_copyvalue(res, xres); if (xres != node->value) aml_delref(&xres, "evalnode"); } if (aml_error) { printf("error evaluating: %s\n", aml_nodename(node)); return (-1); } return (0); } int aml_node_setval(struct acpi_softc *sc, struct aml_node *node, int64_t val) { struct aml_value env; if (!node) return (0); memset(&env, 0, sizeof(env)); env.type = AML_OBJTYPE_INTEGER; env.v_integer = val; return aml_evalnode(sc, node, 1, &env, NULL); } /* * evaluate an AML name * Returns a copy of the value in res (must be freed by user) */ int aml_evalname(struct acpi_softc *sc, struct aml_node *parent, const char *name, int argc, struct aml_value *argv, struct aml_value *res) { parent = aml_searchname(parent, name); return aml_evalnode(sc, parent, argc, argv, res); } /* * evaluate an AML integer object */ int aml_evalinteger(struct acpi_softc *sc, struct aml_node *parent, const char *name, int argc, struct aml_value *argv, int64_t *ival) { struct aml_value res; int rc; parent = aml_searchname(parent, name); rc = aml_evalnode(sc, parent, argc, argv, &res); if (rc == 0) { *ival = aml_val2int(&res); aml_freevalue(&res); } return rc; } /* * Search for an AML name in namespace.. root only */ struct aml_node * __aml_searchname(struct aml_node *root, const void *vname, int create) { char *name = (char *)vname; char nseg[AML_NAMESEG_LEN + 1]; int i; dnprintf(25,"Searchname: %s:%s = ", aml_nodename(root), name); while (*name == AMLOP_ROOTCHAR) { root = &aml_root; name++; } while (*name != 0) { /* Ugh.. we can have short names here: append '_' */ strlcpy(nseg, "____", sizeof(nseg)); for (i=0; i < AML_NAMESEG_LEN && *name && *name != '.'; i++) nseg[i] = *name++; if (*name == '.') name++; root = __aml_search(root, nseg, create); } dnprintf(25,"%p %s\n", root, aml_nodename(root)); return root; } struct aml_node * aml_searchname(struct aml_node *root, const void *vname) { return __aml_searchname(root, vname, 0); } /* * Search for relative name */ struct aml_node * aml_searchrel(struct aml_node *root, const void *vname) { struct aml_node *res; while (root) { res = aml_searchname(root, vname); if (res != NULL) return res; root = root->parent; } return NULL; } #ifndef SMALL_KERNEL void acpi_getdevlist(struct acpi_devlist_head *list, struct aml_node *root, struct aml_value *pkg, int off) { struct acpi_devlist *dl; struct aml_node *node; int idx; for (idx=off; idxlength; idx++) { node = aml_searchname(root, pkg->v_package[idx]->v_string); if (node) { dl = acpi_os_malloc(sizeof(*dl)); if (dl) { dl->dev_node = node; TAILQ_INSERT_TAIL(list, dl, dev_link); } } } } void acpi_freedevlist(struct acpi_devlist_head *list) { struct acpi_devlist *dl; while ((dl = TAILQ_FIRST(list)) != NULL) { TAILQ_REMOVE(list, dl, dev_link); acpi_os_free(dl); } } #endif /* SMALL_KERNEL */