ACPICA: FADT parsing changes and fixes
authorBob Moore <robert.moore@intel.com>
Tue, 30 Dec 2008 19:06:06 +0000 (03:06 +0800)
committerLen Brown <len.brown@intel.com>
Wed, 31 Dec 2008 06:17:09 +0000 (01:17 -0500)
1) Update the register lengths for the PM1 event blocks. The
length must be divided by two in order to use these to access
the status registers.
2) Add run-time option to use default register lengths to override a
faulty FADT.
3) Add warning message if any of the X64 address structures contain a length
that does not match the legacy length earlier in the FADT.
4) Move all FADT warning messages into the ValidateFadt function.

Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
drivers/acpi/tables/tbfadt.c
include/acpi/acglobal.h
include/acpi/actypes.h

index 57e089f..b4ce207 100644 (file)
@@ -51,7 +51,7 @@ ACPI_MODULE_NAME("tbfadt")
 /* Local prototypes */
 static inline void
 acpi_tb_init_generic_address(struct acpi_generic_address *generic_address,
-                            u8 byte_width, u64 address);
+                            u8 space_id, u8 byte_width, u64 address);
 
 static void acpi_tb_convert_fadt(void);
 
@@ -125,7 +125,7 @@ static struct acpi_fadt_info fadt_info_table[] = {
 
 static inline void
 acpi_tb_init_generic_address(struct acpi_generic_address *generic_address,
-                            u8 byte_width, u64 address)
+                            u8 space_id, u8 byte_width, u64 address)
 {
 
        /*
@@ -136,10 +136,10 @@ acpi_tb_init_generic_address(struct acpi_generic_address *generic_address,
 
        /* All other fields are byte-wide */
 
-       generic_address->space_id = ACPI_ADR_SPACE_SYSTEM_IO;
-       generic_address->bit_width = byte_width << 3;
+       generic_address->space_id = space_id;
+       generic_address->bit_width = (u8)ACPI_MUL_8(byte_width);
        generic_address->bit_offset = 0;
-       generic_address->access_width = 0;
+       generic_address->access_width = 0;      /* Access width ANY */
 }
 
 /*******************************************************************************
@@ -226,7 +226,8 @@ void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length)
         */
        if (length > sizeof(struct acpi_table_fadt)) {
                ACPI_WARNING((AE_INFO,
-                             "FADT (revision %u) is longer than ACPI 2.0 version, truncating length 0x%X to 0x%zX",
+                             "FADT (revision %u) is longer than ACPI 2.0 version, "
+                             "truncating length 0x%X to 0x%zX",
                              table->revision, (unsigned)length,
                              sizeof(struct acpi_table_fadt)));
        }
@@ -245,7 +246,6 @@ void acpi_tb_create_local_fadt(struct acpi_table_header *table, u32 length)
         * 2) Validate some of the important values within the FADT
         */
        acpi_tb_convert_fadt();
-       acpi_tb_validate_fadt();
 }
 
 /*******************************************************************************
@@ -337,7 +337,11 @@ static void acpi_tb_convert_fadt(void)
                /* Expand only if the X target is null */
 
                if (!target->address) {
+
+                       /* The space_id is always I/O for the legacy address fields */
+
                        acpi_tb_init_generic_address(target,
+                                                    ACPI_ADR_SPACE_SYSTEM_IO,
                                                     *ACPI_ADD_PTR(u8,
                                                                   &acpi_gbl_FADT,
                                                                   fadt_info_table
@@ -350,6 +354,25 @@ static void acpi_tb_convert_fadt(void)
                }
        }
 
+       /* Validate FADT values now, before we make any changes */
+
+       acpi_tb_validate_fadt();
+
+       /*
+        * Get the length of the individual PM1 registers. Each register is
+        * defined to be the event block length / 2.
+        */
+       pm1_register_length = (u8)ACPI_DIV_2(acpi_gbl_FADT.pm1_event_length);
+
+       /*
+        * Adjust the lengths of the PM1 Event Blocks so that they can be used to
+        * access the PM1 status register(s).
+        */
+       acpi_gbl_FADT.xpm1a_event_block.bit_width =
+           (u8)ACPI_MUL_8(pm1_register_length);
+       acpi_gbl_FADT.xpm1b_event_block.bit_width =
+           (u8)ACPI_MUL_8(pm1_register_length);
+
        /*
         * Calculate separate GAS structs for the PM1 Enable registers.
         * These addresses do not appear (directly) in the FADT, so it is
@@ -370,11 +393,11 @@ static void acpi_tb_convert_fadt(void)
                       " PM1_EVT_LEN (%u)\n",
                       acpi_gbl_FADT.xpm1a_event_block.bit_width,
                       acpi_gbl_FADT.pm1_event_length);
-       pm1_register_length = (u8) ACPI_DIV_2(acpi_gbl_FADT.pm1_event_length);
 
        /* The PM1A register block is required */
 
        acpi_tb_init_generic_address(&acpi_gbl_xpm1a_enable,
+                                    acpi_gbl_FADT.xpm1a_event_block.space_id,
                                     pm1_register_length,
                                     (acpi_gbl_FADT.xpm1a_event_block.address +
                                      pm1_register_length));
@@ -393,6 +416,7 @@ static void acpi_tb_convert_fadt(void)
                               acpi_gbl_FADT.xpm1b_event_block.bit_width,
                               acpi_gbl_FADT.pm1_event_length);
                acpi_tb_init_generic_address(&acpi_gbl_xpm1b_enable,
+                                            acpi_gbl_FADT.xpm1b_event_block.space_id,
                                             pm1_register_length,
                                             (acpi_gbl_FADT.xpm1b_event_block.
                                              address + pm1_register_length));
@@ -401,6 +425,30 @@ static void acpi_tb_convert_fadt(void)
                    acpi_gbl_FADT.xpm1b_event_block.space_id;
 
        }
+
+       if (acpi_gbl_use_default_register_widths) {
+               /*
+                * Optionally, use the default sizes for the ACPI registers.
+                * Some FADTs do not have the correct length(s).
+                *
+                * Note: Xpm1a_event_block and Xpm1b_event_block are used to access the PM1
+                * status registers. The PM1 enable registers are created above.
+                */
+               acpi_gbl_xpm1a_enable.bit_width = ACPI_PM1_REGISTER_WIDTH;
+               acpi_gbl_xpm1b_enable.bit_width = ACPI_PM1_REGISTER_WIDTH;
+
+               acpi_gbl_FADT.xpm1a_event_block.bit_width =
+                   ACPI_PM1_REGISTER_WIDTH;
+               acpi_gbl_FADT.xpm1b_event_block.bit_width =
+                   ACPI_PM1_REGISTER_WIDTH;
+               acpi_gbl_FADT.xpm1a_control_block.bit_width =
+                   ACPI_PM1_REGISTER_WIDTH;
+               acpi_gbl_FADT.xpm1b_control_block.bit_width =
+                   ACPI_PM1_REGISTER_WIDTH;
+               acpi_gbl_FADT.xpm2_control_block.bit_width =
+                   ACPI_PM2_REGISTER_WIDTH;
+               acpi_gbl_FADT.xpm_timer_block.bit_width = ACPI_PM_TIMER_WIDTH;
+       }
 }
 
 /******************************************************************************
@@ -425,26 +473,63 @@ static void acpi_tb_convert_fadt(void)
 
 static void acpi_tb_validate_fadt(void)
 {
+       char *name;
        u32 *address32;
        struct acpi_generic_address *address64;
        u8 length;
        u32 i;
 
-       /* Examine all of the 64-bit extended address fields (X fields) */
+       /*
+        * Check for FACS and DSDT address mismatches. An address mismatch between
+        * the 32-bit and 64-bit address fields (FIRMWARE_CTRL/X_FIRMWARE_CTRL and
+        * DSDT/X_DSDT) would indicate the presence of two FACS or two DSDT tables.
+        */
+       if (acpi_gbl_FADT.facs &&
+           (acpi_gbl_FADT.Xfacs != (u64) acpi_gbl_FADT.facs)) {
+               ACPI_WARNING((AE_INFO,
+                             "32/64 FACS address mismatch in FADT - "
+                             "two FACS tables! %8.8X/%8.8X%8.8X",
+                             acpi_gbl_FADT.facs,
+                             ACPI_FORMAT_UINT64(acpi_gbl_FADT.Xfacs)));
+       }
 
-       for (i = 0; i < ACPI_FADT_INFO_ENTRIES; i++) {
+       if (acpi_gbl_FADT.dsdt &&
+           (acpi_gbl_FADT.Xdsdt != (u64) acpi_gbl_FADT.dsdt)) {
+               ACPI_WARNING((AE_INFO,
+                             "32/64 DSDT address mismatch in FADT - "
+                             "two DSDT tables! %8.8X/%8.8X%8.8X",
+                             acpi_gbl_FADT.dsdt,
+                             ACPI_FORMAT_UINT64(acpi_gbl_FADT.Xdsdt)));
+       }
 
-               /* Generate pointers to the 32-bit and 64-bit addresses and get the length */
+       /* Examine all of the 64-bit extended address fields (X fields) */
 
-               address64 =
-                   ACPI_ADD_PTR(struct acpi_generic_address, &acpi_gbl_FADT,
-                                fadt_info_table[i].target);
+       for (i = 0; i < ACPI_FADT_INFO_ENTRIES; i++) {
+               /*
+                * Generate pointers to the 32-bit and 64-bit addresses, get the
+                * register length (width), and the register name
+                */
+               address64 = ACPI_ADD_PTR(struct acpi_generic_address,
+                                        &acpi_gbl_FADT,
+                                        fadt_info_table[i].target);
                address32 =
                    ACPI_ADD_PTR(u32, &acpi_gbl_FADT,
                                 fadt_info_table[i].source);
                length =
                    *ACPI_ADD_PTR(u8, &acpi_gbl_FADT,
                                  fadt_info_table[i].length);
+               name = fadt_info_table[i].name;
+
+               /*
+                * For each extended field, check for length mismatch between the
+                * legacy length field and the corresonding 64-bit X length field.
+                */
+               if (address64 && (address64->bit_width != ACPI_MUL_8(length))) {
+                       ACPI_WARNING((AE_INFO,
+                                     "32/64X bit length mismatch in %s: %d/%d",
+                                     name, ACPI_MUL_8(length),
+                                     address64->bit_width));
+               }
 
                if (fadt_info_table[i].type & ACPI_FADT_REQUIRED) {
                        /*
@@ -453,8 +538,8 @@ static void acpi_tb_validate_fadt(void)
                         */
                        if (!address64->address || !length) {
                                ACPI_ERROR((AE_INFO,
-                                           "Required field \"%s\" has zero address and/or length: %8.8X%8.8X/%X",
-                                           fadt_info_table[i].name,
+                                           "Required field %s has zero address and/or length: %8.8X%8.8X/%X",
+                                           name,
                                            ACPI_FORMAT_UINT64(address64->
                                                               address),
                                            length));
@@ -467,8 +552,8 @@ static void acpi_tb_validate_fadt(void)
                        if ((address64->address && !length)
                            || (!address64->address && length)) {
                                ACPI_WARNING((AE_INFO,
-                                             "Optional field \"%s\" has zero address or length: %8.8X%8.8X/%X",
-                                             fadt_info_table[i].name,
+                                             "Optional field %s has zero address or length: %8.8X%8.8X/%X",
+                                             name,
                                              ACPI_FORMAT_UINT64(address64->
                                                                 address),
                                              length));
@@ -480,8 +565,8 @@ static void acpi_tb_validate_fadt(void)
                if (address64->address && *address32 &&
                    (address64->address != (u64) * address32)) {
                        ACPI_ERROR((AE_INFO,
-                                   "32/64X address mismatch in \"%s\": [%8.8X] [%8.8X%8.8X], using 64X",
-                                   fadt_info_table[i].name, *address32,
+                                   "32/64X address mismatch in %s: [%8.8X] [%8.8X%8.8X], using 64X",
+                                   name, *address32,
                                    ACPI_FORMAT_UINT64(address64->address)));
                }
        }
index 78f3c14..55eb5d6 100644 (file)
@@ -102,6 +102,12 @@ ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_create_osi_method, TRUE);
  */
 ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_leave_wake_gpes_disabled, TRUE);
 
+/*
+ * Optionally use default values for the ACPI register widths. Set this to
+ * TRUE to use the defaults, if an FADT contains incorrect widths/lengths.
+ */
+ACPI_EXTERN u8 ACPI_INIT_GLOBAL(acpi_gbl_use_default_register_widths, FALSE);
+
 /*****************************************************************************
  *
  * Debug support
index 24b2cef..03744d2 100644 (file)
@@ -309,10 +309,16 @@ typedef u32 acpi_physical_address;
  *
  *****************************************************************************/
 
-/* Number of distinct GPE register blocks and register width */
+/* Number of distinct FADT-based GPE register blocks (GPE0 and GPE1) */
 
 #define ACPI_MAX_GPE_BLOCKS             2
+
+/* Default ACPI register widths */
+
 #define ACPI_GPE_REGISTER_WIDTH         8
+#define ACPI_PM1_REGISTER_WIDTH         16
+#define ACPI_PM2_REGISTER_WIDTH         8
+#define ACPI_PM_TIMER_WIDTH             32
 
 /* Names within the namespace are 4 bytes long */