ACPI_MODULE_NAME("tbutils")
 
 /* Local prototypes */
+static acpi_status acpi_tb_validate_xsdt(acpi_physical_address address);
+
 static acpi_physical_address
 acpi_tb_get_root_table_entry(u8 *table_entry, u32 table_entry_size);
 
-/*******************************************************************************
- *
- * FUNCTION:    acpi_tb_check_xsdt
- *
- * PARAMETERS:  address                    - Pointer to the XSDT
- *
- * RETURN:      status
- *             AE_OK - XSDT is okay
- *             AE_NO_MEMORY - can't map XSDT
- *             AE_INVALID_TABLE_LENGTH - invalid table length
- *             AE_NULL_ENTRY - XSDT has NULL entry
- *
- * DESCRIPTION: validate XSDT
-******************************************************************************/
-
-static acpi_status
-acpi_tb_check_xsdt(acpi_physical_address address)
-{
-       struct acpi_table_header *table;
-       u32 length;
-       u64 xsdt_entry_address;
-       u8 *table_entry;
-       u32 table_count;
-       int i;
-
-       table = acpi_os_map_memory(address, sizeof(struct acpi_table_header));
-       if (!table)
-               return AE_NO_MEMORY;
-
-       length = table->length;
-       acpi_os_unmap_memory(table, sizeof(struct acpi_table_header));
-       if (length < sizeof(struct acpi_table_header))
-               return AE_INVALID_TABLE_LENGTH;
-
-       table = acpi_os_map_memory(address, length);
-       if (!table)
-               return AE_NO_MEMORY;
-
-       /* Calculate the number of tables described in XSDT */
-       table_count =
-               (u32) ((table->length -
-               sizeof(struct acpi_table_header)) / sizeof(u64));
-       table_entry =
-               ACPI_CAST_PTR(u8, table) + sizeof(struct acpi_table_header);
-       for (i = 0; i < table_count; i++) {
-               ACPI_MOVE_64_TO_64(&xsdt_entry_address, table_entry);
-               if (!xsdt_entry_address) {
-                       /* XSDT has NULL entry */
-                       break;
-               }
-               table_entry += sizeof(u64);
-       }
-       acpi_os_unmap_memory(table, length);
-
-       if (i < table_count)
-               return AE_NULL_ENTRY;
-       else
-               return AE_OK;
-}
-
 #if (!ACPI_REDUCED_HARDWARE)
 /*******************************************************************************
  *
         * Get the table physical address (32-bit for RSDT, 64-bit for XSDT):
         * Note: Addresses are 32-bit aligned (not 64) in both RSDT and XSDT
         */
-       if (table_entry_size == sizeof(u32)) {
+       if (table_entry_size == ACPI_RSDT_ENTRY_SIZE) {
                /*
                 * 32-bit platform, RSDT: Return 32-bit table entry
                 * 64-bit platform, RSDT: Expand 32-bit to 64-bit and return
        }
 }
 
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_tb_validate_xsdt
+ *
+ * PARAMETERS:  address             - Physical address of the XSDT (from RSDP)
+ *
+ * RETURN:      Status. AE_OK if the table appears to be valid.
+ *
+ * DESCRIPTION: Validate an XSDT to ensure that it is of minimum size and does
+ *              not contain any NULL entries. A problem that is seen in the
+ *              field is that the XSDT exists, but is actually useless because
+ *              of one or more (or all) NULL entries.
+ *
+ ******************************************************************************/
+
+static acpi_status acpi_tb_validate_xsdt(acpi_physical_address xsdt_address)
+{
+       struct acpi_table_header *table;
+       u8 *next_entry;
+       acpi_physical_address address;
+       u32 length;
+       u32 entry_count;
+       acpi_status status;
+       u32 i;
+
+       /* Get the XSDT length */
+
+       table =
+           acpi_os_map_memory(xsdt_address, sizeof(struct acpi_table_header));
+       if (!table) {
+               return (AE_NO_MEMORY);
+       }
+
+       length = table->length;
+       acpi_os_unmap_memory(table, sizeof(struct acpi_table_header));
+
+       /*
+        * Minimum XSDT length is the size of the standard ACPI header
+        * plus one physical address entry
+        */
+       if (length < (sizeof(struct acpi_table_header) + ACPI_XSDT_ENTRY_SIZE)) {
+               return (AE_INVALID_TABLE_LENGTH);
+       }
+
+       /* Map the entire XSDT */
+
+       table = acpi_os_map_memory(xsdt_address, length);
+       if (!table) {
+               return (AE_NO_MEMORY);
+       }
+
+       /* Get the number of entries and pointer to first entry */
+
+       status = AE_OK;
+       next_entry = ACPI_ADD_PTR(u8, table, sizeof(struct acpi_table_header));
+       entry_count = (u32)((table->length - sizeof(struct acpi_table_header)) /
+                           ACPI_XSDT_ENTRY_SIZE);
+
+       /* Validate each entry (physical address) within the XSDT */
+
+       for (i = 0; i < entry_count; i++) {
+               address =
+                   acpi_tb_get_root_table_entry(next_entry,
+                                                ACPI_XSDT_ENTRY_SIZE);
+               if (!address) {
+
+                       /* Detected a NULL entry, XSDT is invalid */
+
+                       status = AE_NULL_ENTRY;
+                       break;
+               }
+
+               next_entry += ACPI_XSDT_ENTRY_SIZE;
+       }
+
+       /* Unmap table */
+
+       acpi_os_unmap_memory(table, length);
+       return (status);
+}
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_tb_parse_root_table
        u32 table_count;
        struct acpi_table_header *table;
        acpi_physical_address address;
-       acpi_physical_address uninitialized_var(rsdt_address);
        u32 length;
        u8 *table_entry;
        acpi_status status;
 
        ACPI_FUNCTION_TRACE(tb_parse_root_table);
 
-       /*
-        * Map the entire RSDP and extract the address of the RSDT or XSDT
-        */
+       /* Map the entire RSDP and extract the address of the RSDT or XSDT */
+
        rsdp = acpi_os_map_memory(rsdp_address, sizeof(struct acpi_table_rsdp));
        if (!rsdp) {
                return_ACPI_STATUS(AE_NO_MEMORY);
 
        /* Differentiate between RSDT and XSDT root tables */
 
-       if (rsdp->revision > 1 && rsdp->xsdt_physical_address
+       if ((rsdp->revision > 1) && rsdp->xsdt_physical_address
                        && !acpi_rsdt_forced) {
                /*
-                * Root table is an XSDT (64-bit physical addresses). We must use the
-                * XSDT if the revision is > 1 and the XSDT pointer is present, as per
-                * the ACPI specification.
+                * RSDP contains an XSDT (64-bit physical addresses). We must use
+                * the XSDT if the revision is > 1 and the XSDT pointer is present,
+                * as per the ACPI specification.
                 */
                address = (acpi_physical_address) rsdp->xsdt_physical_address;
-               table_entry_size = sizeof(u64);
-               rsdt_address = (acpi_physical_address)
-                                       rsdp->rsdt_physical_address;
+               table_entry_size = ACPI_XSDT_ENTRY_SIZE;
        } else {
                /* Root table is an RSDT (32-bit physical addresses) */
 
                address = (acpi_physical_address) rsdp->rsdt_physical_address;
-               table_entry_size = sizeof(u32);
+               table_entry_size = ACPI_RSDT_ENTRY_SIZE;
        }
 
        /*
         */
        acpi_os_unmap_memory(rsdp, sizeof(struct acpi_table_rsdp));
 
-       if (table_entry_size == sizeof(u64)) {
-               if (acpi_tb_check_xsdt(address) == AE_NULL_ENTRY) {
-                       /* XSDT has NULL entry, RSDT is used */
-                       address = rsdt_address;
-                       table_entry_size = sizeof(u32);
-                       ACPI_WARNING((AE_INFO, "BIOS XSDT has NULL entry, "
-                                       "using RSDT"));
+       /*
+        * If it is present, validate the XSDT for access/size and ensure
+        * that all table entries are at least non-NULL
+        */
+       if (table_entry_size == ACPI_XSDT_ENTRY_SIZE) {
+               status = acpi_tb_validate_xsdt(address);
+               if (ACPI_FAILURE(status)) {
+                       ACPI_BIOS_WARNING((AE_INFO,
+                                          "XSDT is invalid (%s), using RSDT",
+                                          acpi_format_exception(status)));
+
+                       /* Fall back to the RSDT */
+
+                       address =
+                           (acpi_physical_address) rsdp->rsdt_physical_address;
+                       table_entry_size = ACPI_RSDT_ENTRY_SIZE;
                }
        }
+
        /* Map the RSDT/XSDT table header to get the full table length */
 
        table = acpi_os_map_memory(address, sizeof(struct acpi_table_header));
 
        acpi_tb_print_table_header(address, table);
 
-       /* Get the length of the full table, verify length and map entire table */
-
+       /*
+        * Validate length of the table, and map entire table.
+        * Minimum length table must contain at least one entry.
+        */
        length = table->length;
        acpi_os_unmap_memory(table, sizeof(struct acpi_table_header));
 
-       if (length < sizeof(struct acpi_table_header)) {
+       if (length < (sizeof(struct acpi_table_header) + table_entry_size)) {
                ACPI_BIOS_ERROR((AE_INFO,
                                 "Invalid table length 0x%X in RSDT/XSDT",
                                 length));
                return_ACPI_STATUS(status);
        }
 
-       /* Calculate the number of tables described in the root table */
+       /* Get the number of entries and pointer to first entry */
 
        table_count = (u32)((table->length - sizeof(struct acpi_table_header)) /
                            table_entry_size);
+       table_entry = ACPI_ADD_PTR(u8, table, sizeof(struct acpi_table_header));
+
        /*
         * First two entries in the table array are reserved for the DSDT
         * and FACS, which are not actually present in the RSDT/XSDT - they
         * come from the FADT
         */
-       table_entry =
-           ACPI_CAST_PTR(u8, table) + sizeof(struct acpi_table_header);
        acpi_gbl_root_table_list.current_table_count = 2;
 
-       /*
-        * Initialize the root table array from the RSDT/XSDT
-        */
+       /* Initialize the root table array from the RSDT/XSDT */
+
        for (i = 0; i < table_count; i++) {
                if (acpi_gbl_root_table_list.current_table_count >=
                    acpi_gbl_root_table_list.max_table_count) {
                acpi_tb_install_table(acpi_gbl_root_table_list.tables[i].
                                      address, NULL, i);
 
-               /* Special case for FADT - get the DSDT and FACS */
+               /* Special case for FADT - validate it then get the DSDT and FACS */
 
                if (ACPI_COMPARE_NAME
                    (&acpi_gbl_root_table_list.tables[i].signature,