From 01fedb06ff6ab3ce4c8655e988a0577b93bc6253 Mon Sep 17 00:00:00 2001
From: Valentin Dornauer <valentin@unimplemented.org>
Date: Tue, 1 Jul 2014 01:49:52 +0200
Subject: [PATCH] Fix ACPI halt for certain DSDTs

The AML parser implements only a small subset of possible AML
opcodes. On the Fujitsu Lifebook E744 this and another bug in
the parser (incorrect handling of TermArg data types) would lead
to the laptop not turning off (_S5 not found).

* grub-core/commands/acpihalt.c: Support OpAlias in the AML parser;
in skip_ext_op(), correctly parse OpRegionOp (TermArgs aren't always
simply strings!); add function to skip TermArgs.
* include/grub/acpi.h: Add new opcodes
---
 ChangeLog                     |   12 +++++++++
 grub-core/commands/acpihalt.c |   55 +++++++++++++++++++++++++++++++++++++++--
 include/grub/acpi.h           |   14 +++++++++++
 3 files changed, 79 insertions(+), 2 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 5109c5a..5433b12 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2014-06-30  Valentin Dornauer  <valentin@unimplemented.org>
+
+	The AML parser implements only a small subset of possible AML
+	opcodes. On the Fujitsu Lifebook E744 this and another bug in
+	the parser (incorrect handling of TermArg data types) would lead
+	to the laptop not turning off (_S5 not found).
+
+	* grub-core/commands/acpihalt.c: Support OpAlias in the AML parser;
+	in skip_ext_op(), correctly parse OpRegionOp (TermArgs aren't always
+	simply strings!); add function to skip TermArgs.
+	* include/grub/acpi.h: Add new opcodes
+
 2014-06-26  Colin Watson  <cjwatson@ubuntu.com>
 
 	* docs/grub-dev.texi (Finding your way around): The build system no
diff --git a/grub-core/commands/acpihalt.c b/grub-core/commands/acpihalt.c
index 83bdfe1..beb1045 100644
--- a/grub-core/commands/acpihalt.c
+++ b/grub-core/commands/acpihalt.c
@@ -136,6 +136,45 @@ skip_data_ref_object (const grub_uint8_t *ptr, const grub_uint8_t *end)
 }
 
 static inline grub_uint32_t
+skip_term (const grub_uint8_t *ptr, const grub_uint8_t *end)
+{
+  grub_uint32_t add;
+  const grub_uint8_t *ptr0 = ptr;
+
+  switch(*ptr)
+  {
+    case GRUB_ACPI_OPCODE_ADD:
+    case GRUB_ACPI_OPCODE_CONCAT:
+    case GRUB_ACPI_OPCODE_SUBTRACT:
+    case GRUB_ACPI_OPCODE_MULTIPLY:
+    case GRUB_ACPI_OPCODE_LSHIFT:
+    case GRUB_ACPI_OPCODE_RSHIFT:
+    case GRUB_ACPI_OPCODE_AND:
+    case GRUB_ACPI_OPCODE_NAND:
+    case GRUB_ACPI_OPCODE_OR:
+    case GRUB_ACPI_OPCODE_NOR:
+    case GRUB_ACPI_OPCODE_XOR:
+    case GRUB_ACPI_OPCODE_CONCATRES:
+      /*
+       * Parameters for these opcodes: TermArg, TermArg Target, see ACPI
+       * spec r5.0, page 828f.
+       */
+      ptr++;
+      ptr += add = skip_term (ptr, end);
+      if (add == 0)
+        return 0;
+      ptr += add = skip_term (ptr, end);
+      if (add == 0)
+        return 0;
+      ptr += skip_name_string (ptr, end);
+      break;
+    default:
+      return skip_data_ref_object (ptr, end);
+  }
+  return ptr - ptr0;
+}
+
+static inline grub_uint32_t
 skip_ext_op (const grub_uint8_t *ptr, const grub_uint8_t *end)
 {
   const grub_uint8_t *ptr0 = ptr;
@@ -156,10 +195,10 @@ skip_ext_op (const grub_uint8_t *ptr, const grub_uint8_t *end)
       ptr++;
       ptr += skip_name_string (ptr, end);
       ptr++;
-      ptr += add = skip_data_ref_object (ptr, end);
+      ptr += add = skip_term (ptr, end);
       if (!add)
 	return 0;
-      ptr += add = skip_data_ref_object (ptr, end);
+      ptr += add = skip_term (ptr, end);
       if (!add)
 	return 0;
       break;
@@ -180,6 +219,7 @@ skip_ext_op (const grub_uint8_t *ptr, const grub_uint8_t *end)
   return ptr - ptr0;
 }
 
+
 static int
 get_sleep_type (grub_uint8_t *table, grub_uint8_t *ptr, grub_uint8_t *end,
 		grub_uint8_t *scope, int scope_len)
@@ -250,6 +290,17 @@ get_sleep_type (grub_uint8_t *table, grub_uint8_t *ptr, grub_uint8_t *end,
 	  if (!add)
 	    return -1;
 	  break;
+	case GRUB_ACPI_OPCODE_ALIAS:
+	  ptr++;
+	  /* We need to skip two name strings */
+	  ptr += add = skip_name_string (ptr, end);
+	  if (!add)
+	    return -1;
+	  ptr += add = skip_name_string (ptr, end);
+	  if (!add)
+	    return -1;
+	  break;
+
 	case GRUB_ACPI_OPCODE_SCOPE:
 	  {
 	    int scope_sleep_type;
diff --git a/include/grub/acpi.h b/include/grub/acpi.h
index 2ac2bd6..7ec0c30 100644
--- a/include/grub/acpi.h
+++ b/include/grub/acpi.h
@@ -192,6 +192,7 @@ enum
   {
     GRUB_ACPI_OPCODE_ZERO = 0, GRUB_ACPI_OPCODE_ONE = 1,
     GRUB_ACPI_OPCODE_NAME = 8, GRUB_ACPI_OPCODE_BYTE_CONST = 0x0a,
+    GRUB_ACPI_OPCODE_ALIAS = 0x06,
     GRUB_ACPI_OPCODE_WORD_CONST = 0x0b,
     GRUB_ACPI_OPCODE_DWORD_CONST = 0x0c,
     GRUB_ACPI_OPCODE_STRING_CONST = 0x0d,
@@ -201,6 +202,19 @@ enum
     GRUB_ACPI_OPCODE_METHOD = 0x14, GRUB_ACPI_OPCODE_EXTOP = 0x5b,
     GRUB_ACPI_OPCODE_CREATE_WORD_FIELD = 0x8b,
     GRUB_ACPI_OPCODE_CREATE_BYTE_FIELD = 0x8c,
+    GRUB_ACPI_OPCODE_ADD = 0x72,
+    GRUB_ACPI_OPCODE_CONCAT = 0x73,
+    GRUB_ACPI_OPCODE_SUBTRACT = 0x74,
+    GRUB_ACPI_OPCODE_MULTIPLY = 0x77,
+    GRUB_ACPI_OPCODE_DIVIDE = 0x78,
+    GRUB_ACPI_OPCODE_LSHIFT = 0x79,
+    GRUB_ACPI_OPCODE_RSHIFT = 0x7a,
+    GRUB_ACPI_OPCODE_AND = 0x7b,
+    GRUB_ACPI_OPCODE_NAND = 0x7c,
+    GRUB_ACPI_OPCODE_OR = 0x7d,
+    GRUB_ACPI_OPCODE_NOR = 0x7e,
+    GRUB_ACPI_OPCODE_XOR = 0x7f,
+    GRUB_ACPI_OPCODE_CONCATRES = 0x80,
     GRUB_ACPI_OPCODE_IF = 0xa0, GRUB_ACPI_OPCODE_ONES = 0xff
   };
 enum
-- 
1.7.10.4

