/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.driver.system.acpi.aml; import java.nio.ByteBuffer; import org.apache.log4j.Logger; /** * Parser. * <p/> * <p> * Title: * </p> * <p> * Description: * </p> * <p> * Copyright: Copyright (c) 2003 * </p> * <p> * Company: * </p> * * @author not attributable * @version 1.0 */ public class Parser { private static final Logger log = Logger.getLogger(Parser.class); private class ReferencedInteger { int value; public ReferencedInteger() { } public ReferencedInteger(int value) { this.value = value; } public int increment() { value++; return value; } public int decrement() { value--; return value; } public void set(int value) { this.value = value; } public int get() { return value; } } private class Scope { ParseNode originNode; // current op being parsed ByteBuffer buffer; String argsFormat; int next_arg; // next argument to parse in argsFormat String int arg_count = 0; // # fixed arguments int arg_end; // current argument end int pkg_end; // current package end Scope parent = null; // parent scope public Scope() { } public Scope(acpi_parse_state state, ParseNode scopeRoot, String argsFormat, int next_arg, int arg_count) { this.originNode = scopeRoot; this.buffer = state.buffer.duplicate(); this.arg_count = arg_count; this.argsFormat = argsFormat; this.next_arg = next_arg; this.arg_end = (arg_count == Aml.ACPI_VAR_ARGS) ? state.pkg_end : Aml.ACPI_MAX_AML; this.pkg_end = state.pkg_end; } public ParseNode getOp() { return originNode; } } private class acpi_parse_state { ByteBuffer buffer; Scope scope; // current scope Scope scope_avail; // unused (extra) scope structs int pkg_end; // current package end public acpi_parse_state(ByteBuffer buf) { this.buffer = buf; this.pkg_end = buf.limit(); } public void acpi_cleanup_scope() { // destroy available list while (scope_avail != null) { Scope scopeTmp = scope_avail; scope_avail = scopeTmp.parent; scopeTmp = null; } // destroy scope stack while (scope != null) { //Scope scopeTmp = scope; scope = scope.parent; //scopeTmp = null; } } public ParseNode acpi_pop_scope(Scope newScope) { Scope prevScope = scope; ParseNode node = null; if (prevScope.parent != null) { // return to parsing previous op node = scope.getOp(); newScope.argsFormat = scope.argsFormat; newScope.next_arg = scope.next_arg; pkg_end = prevScope.pkg_end; this.scope = prevScope.parent; // add scope to available list prevScope.parent = this.scope_avail; this.scope_avail = prevScope; } else { // empty parse stack, prepare to fetch next opcode newScope.next_arg = 0; } return node; } public void acpi_push_scope(ParseNode pushedNode, String argsFormat, int next_arg, int arg_count) { Scope newScope = this.scope_avail; if (newScope != null) { // grabbed scope from available list scope_avail = newScope.parent; } else { // allocate scope from the heap newScope = new Scope(); } newScope.originNode = pushedNode; newScope.buffer = buffer.duplicate(); newScope.argsFormat = argsFormat; newScope.next_arg = next_arg; newScope.arg_count = arg_count; newScope.arg_end = (arg_count == Aml.ACPI_VAR_ARGS) ? pkg_end : Aml.ACPI_MAX_AML; newScope.pkg_end = pkg_end; newScope.parent = this.scope; this.scope = newScope; } public void acpi_next_namepath(ParseNode arg, ReferencedInteger arg_count, boolean method_calls) { NameString path = acpi_next_namestring(); ParseNode method = null; if (method_calls) { method = acpi_get_parent_scope().find(path, Aml.AML_METHOD, false); } if (method != null) { // method call ParseNode count = method.getArg(0); if (count != null && count.opcode == Aml.AML_BYTECONST) { ParseNode name = new ParseNode(Aml.AML_NAMEPATH); if (name != null) { arg.opcode = Aml.AML_METHODCALL; name.opcode = Aml.AML_NAMEPATH; name.setName(path); arg.appendArg(name); arg_count.set(((Integer) count.value).intValue() & Aml.ACPI_METHOD_ARG_MASK); } } } else { // variable/name access arg.opcode = Aml.AML_NAMEPATH; arg.setName(path); } } public boolean acpi_has_completed_scope() { return buffer.position() >= scope.arg_end || scope.arg_count == 0; } public ParseNode acpi_get_parent_scope() { return scope.getOp(); } public byte peekByte() { return buffer.get(buffer.position()); } public int peekByteInt() { return (buffer.get(buffer.position()) & 0xff); } public byte getByte() { return buffer.get(); } public int getByteInt() { return (buffer.get()) & 0xff; } public int getWord() { // should use getShort but not implemented in JNode... int value = peekWord(); move(2); return value; } public int getDWord() { int value = peekDWord(); move(4); return value; } public String getNameseg() { StringBuilder buffer = new StringBuilder(4); for (int i = 0; i < 4; i++) buffer.append((char) (getByteInt())); return buffer.toString(); } public int peekWord() { int tmp; tmp = (buffer.get(buffer.position() + 1) & 0xff) << 8; tmp |= buffer.get(buffer.position()) & 0xff; return tmp; } public int peekDWord() { int tmp; tmp = (buffer.get(buffer.position() + 3) & 0xff) << 24; tmp |= (buffer.get(buffer.position() + 2) & 0xff) << 16; tmp |= (buffer.get(buffer.position() + 1) & 0xff) << 8; tmp |= buffer.get(buffer.position()) & 0xff; return tmp; } public int acpi_next_pkg_end() { int start = buffer.position(); int byte1, byte2, byte3, byte4, len = 0; byte1 = getByteInt(); // bits 6-7 contain encoding scheme switch (byte1 >> 6) { case 0: // 1-byte encoding (bits 0-5) len = (byte1 & 0x3f); break; case 1: // 2-byte encoding (next byte + bits 0-3) byte2 = getByteInt(); len = (byte2 << 4) | (byte1 & 0xf); break; case 2: // 3-byte encoding (next 2 bytes + bits 0-3) byte2 = getByteInt(); byte3 = getByteInt(); len = (byte3 << 12) | (byte2 << 4) | (byte1 & 0xf); break; case 3: // 4-byte encoding (next 3 bytes + bits 0-3) byte2 = getByteInt(); byte3 = getByteInt(); byte4 = getByteInt(); len = (byte4 << 20) | (byte3 << 12) | (byte2 << 4) | (byte1 & 0xf); break; } return (start + len); // end of package } ParseNode acpi_next_field() { ParseNode field; int opcode; // determine field type switch (this.peekByte()) { default: // assert(acpi_is_lead(readb(state->aml))); opcode = Aml.AML_NAMEDFIELD; break; case 0x00: opcode = Aml.AML_RESERVEDFIELD; getByte(); break; case 0x01: opcode = Aml.AML_ACCESSFIELD; getByte(); break; } field = new ParseNode(opcode); if (field != null) { int start = buffer.position(); switch (opcode) { case Aml.AML_NAMEDFIELD: { field.setName(getNameseg()); field.value = new Integer(acpi_next_pkg_end() - start); break; } case Aml.AML_RESERVEDFIELD: field.value = new Integer(acpi_next_pkg_end() - start); break; case Aml.AML_ACCESSFIELD: field.value = new Integer(getWord()); break; } } return field; } public int acpi_peek_opcode() { int opcode = peekByteInt(); int nextOpcode = 0; if (buffer.position() < buffer.limit() - 1) { int wordOpcode = peekWord(); nextOpcode = wordOpcode >> 8; } if (opcode == Aml.AML_EXTOP || (opcode == Aml.AML_LNOT && (nextOpcode == Aml.AML_LEQUAL || nextOpcode == Aml.AML_LGREATER || nextOpcode == Aml.AML_LLESS))) { // extended opcode, !=, <=, or >= opcode = opcode << 8 | nextOpcode; } // don't convert bare name to a namepath return opcode; } public ParseNode acpi_next_arg(char argType, ReferencedInteger arg_count) { ParseNode arg = null; switch (argType) { case Aml.AML_BYTEDATA_ARG: case Aml.AML_WORDDATA_ARG: case Aml.AML_DWORDDATA_ARG: case Aml.AML_ASCIICHARLIST_ARG: case Aml.AML_NAMESTRING_ARG: case Aml.AML_NAME_ARG: case Aml.AML_REGIONSPACE_ARG: case Aml.AML_FIELDFLAGS_ARG: case Aml.AML_METHODFLAGS_ARG: case Aml.AML_SYNCFLAGS_ARG: // constants, strings, and namestrings are all the same size arg = new ParseNode(Aml.AML_BYTECONST); acpi_next_simple(arg, argType); break; case Aml.AML_PKGLENGTH_ARG: // package length, nothing returned pkg_end = acpi_next_pkg_end(); break; case Aml.AML_FIELDLIST_ARG: if (buffer.position() < pkg_end) { // non-empty list ParseNode prev = null; while (buffer.position() < pkg_end) { ParseNode field = acpi_next_field(); if (field == null) { break; } if (prev != null) { prev.next = field; } else { arg = field; } prev = field; } // skip to end of byte data buffer.position(pkg_end); } break; case Aml.AML_BYTELIST_ARG: if (buffer.position() < pkg_end) { // non-empty list arg = new ParseNode(Aml.AML_BYTELIST); if (arg != null) { // fill in bytelist data int oldlimit = buffer.limit(); buffer.limit(pkg_end); arg.data = buffer.slice(); buffer.limit(oldlimit); } // skip to end of byte data buffer.position(pkg_end); } break; case Aml.AML_TARGET_ARG: case Aml.AML_SUPERNAME_ARG: { int subop = acpi_peek_opcode(); if (subop == 0 || Aml.isLeadChar((byte) subop) || Aml.isPrefixChar((byte) subop)) { // NullName or NameString arg = new ParseNode(Aml.AML_NAMEPATH); acpi_next_namepath(arg, arg_count, false); } else { // single complex argument, nothing returned arg_count.set(1); } break; } case Aml.AML_DATAOBJECT_ARG: case Aml.AML_TERMARG_ARG: // single complex argument, nothing returned arg_count.set(1); break; case Aml.AML_DATAOBJECTLIST_ARG: case Aml.AML_TERMLIST_ARG: case Aml.AML_OBJECTLIST_ARG: if (buffer.position() < pkg_end) { // non-empty list of variable args, nothing returned arg_count.set(Aml.ACPI_VAR_ARGS); } break; default: // Missing a case above after modifying amlop.txt and mkaml // THROW EXCEPTION *((int *)0) = 0; } return arg; } /* * Get next namestring */ NameString acpi_next_namestring() { NameString ns = new NameString(); StringBuffer prefix = new StringBuffer(); if (Aml.isPrefixChar(peekByte())) { while (Aml.isPrefixChar(peekByte())) { prefix.append((char) getByteInt()); } ns.setPrefix(prefix.toString()); } switch (peekByte()) { case 0: // NullName if (prefix.length() == 0) { ns = null; } move(1); break; case Aml.AML_DUALNAMEPATH: // two name segments move(1); // skips the dual name path indicator ns.addDualNamePath(this.buffer); // buffer is advanced accordingly break; case Aml.AML_MULTINAMEPATH: { // multiple name segments move(1); ns.addMultiNamePath(this.buffer); // buffer is advanced accordingly break; } default: // single name segment // assert(acpi_is_lead(readb(end))); ns.addNamePath(buffer); break; } return ns; } /* * Get next simple argument (constant, string, or namestring) */ ParseNode acpi_next_simple(ParseNode arg, char arg_type) { switch (arg_type) { case Aml.AML_BYTEDATA_ARG: arg.opcode = Aml.AML_BYTECONST; arg.value = new Integer(getByteInt()); break; case Aml.AML_WORDDATA_ARG: arg.opcode = Aml.AML_WORDCONST; arg.value = new Integer(getWord()); break; case Aml.AML_DWORDDATA_ARG: arg.opcode = Aml.AML_DWORDCONST; arg.value = new Integer(getDWord()); break; case Aml.AML_ASCIICHARLIST_ARG: arg.opcode = Aml.AML_STRING; StringBuilder tmp = new StringBuilder(); char c = 0; while ((c = (char) getByte()) != '\0') { tmp.append(c); } arg.value = tmp.toString(); break; case Aml.AML_NAMESTRING_ARG: case Aml.AML_NAME_ARG: arg.opcode = Aml.AML_NAMEPATH; arg.setName(acpi_next_namestring()); break; case Aml.AML_FIELDFLAGS_ARG: arg.opcode = Aml.AML_FIELDFLAGS; arg.value = new Integer(getByteInt()); break; case Aml.AML_REGIONSPACE_ARG: arg.opcode = Aml.AML_REGIONSPACE; arg.value = new Integer(getByteInt()); break; case Aml.AML_METHODFLAGS_ARG: arg.opcode = Aml.AML_METHODFLAGS; arg.value = new Integer(getByteInt()); break; case Aml.AML_SYNCFLAGS_ARG: arg.opcode = Aml.AML_SYNCFLAGS; arg.value = new Integer(getByteInt()); break; } return arg; } public void move(int bytes) { buffer.position(buffer.position() + bytes); } } // END OF PRIVATE INNER CLASS STATE public Parser() { } public void acpi_parse_aml(ParseNode root, ByteBuffer buffer) { acpi_parse_state state = null; // parser state ParseNode currentNode = null; // current op String argsFormat = null; // current op next argument int argsIndex = 0; ReferencedInteger arg_count = new ReferencedInteger(0); // initialize parser state state = new acpi_parse_state(buffer); // adds the root scope state.scope = new Scope(state, root, null, 0, Aml.ACPI_VAR_ARGS); while (state.buffer.position() < state.buffer.limit()) { if (currentNode == null) { int opcode = state.acpi_peek_opcode(); AmlOpcode opc = AmlOpcode.getAmlOpcode(opcode); if (opc != null) { // normal opcode state.move(((opcode & 0xff00) > 0) ? 2 : 1); argsFormat = opc.argsFormat; argsIndex = 0; } else if (Aml.isPrefixChar((byte) opcode) || Aml.isLeadChar((byte) opcode)) { // convert bare name string to namepath opcode = Aml.AML_NAMEPATH; opc = AmlOpcode.getAmlOpcode(opcode); argsFormat = "n"; argsIndex = 0; } else { // skip unknown opcodes state.move(((opcode & 0xff00) > 0) ? 2 : 1); continue; } // create and append to parent's argument list if (Aml.isNamedOpcode(opcode)) { ParseNode preop = null; while (argsIndex < argsFormat.length() && argsFormat.charAt(argsIndex) != Aml.AML_NAME_ARG) { ParseNode arg = state.acpi_next_arg(argsFormat.charAt(argsIndex), arg_count); if (preop == null) preop = arg; else preop.appendArg(arg); argsIndex++; } currentNode = (state.acpi_get_parent_scope()).find(state.acpi_next_namestring(), opcode, true); argsIndex++; if (currentNode != null) currentNode.appendArg(preop); else log.debug("Trying to add to an empty node"); } else { currentNode = new ParseNode(opcode); state.acpi_get_parent_scope().appendArg(currentNode); } if (currentNode == null) { break; } } // if currentNode ==null arg_count.set(0); if (argsFormat != null) { // get arguments switch (currentNode.opcode) { case Aml.AML_BYTECONST: // AML_BYTEDATA_ARG case Aml.AML_WORDCONST: // AML_WORDDATA_ARG case Aml.AML_DWORDCONST: // AML_DWORDATA_ARG case Aml.AML_STRING: // AML_ASCIICHARLIST_ARG // fill in constant or string argument directly state.acpi_next_simple(currentNode, argsFormat.charAt(argsIndex)); break; case Aml.AML_NAMEPATH: state.acpi_next_namepath(currentNode, arg_count, true); argsFormat = null; break; default: // op is not a constant or string // append each argument while (argsIndex < argsFormat.length() && arg_count.get() == 00) { ParseNode arg = state.acpi_next_arg(argsFormat.charAt(argsIndex), arg_count); currentNode.appendArg(arg); argsIndex++; } if (currentNode.opcode == Aml.AML_METHOD) { // skip parsing of method body currentNode.data = state.buffer.duplicate(); currentNode.data.limit(state.pkg_end); currentNode.data = currentNode.data.slice(); currentNode.data.rewind(); try { state.buffer.position(state.pkg_end); } catch (Exception ex) { System.err.println( state.buffer.position() + "," + state.buffer.limit() + ':' + state.pkg_end); } arg_count.set(0); } break; } } if (arg_count.get() == 0) { // completed op, prepare for next state.scope.arg_count--; if (state.acpi_has_completed_scope()) { Scope newScope = new Scope(); currentNode = state.acpi_pop_scope(newScope); argsFormat = newScope.argsFormat; argsIndex = newScope.next_arg; } else { currentNode = null; } } else { // complex argument, push op and prepare for argument state.acpi_push_scope(currentNode, argsFormat, argsIndex, arg_count.get()); currentNode = null; } } // complete any remaining ops state.acpi_cleanup_scope(); } public ParseNode parse(ByteBuffer buffer) { ParseNode root = new ParseNode(Aml.AML_SCOPE); if (root == null) { return root; } //ParseNode op; // parse excluding method bodies acpi_parse_aml(root, buffer); return root; } }