ARM errors just print out the error information value, then the
value needs to be manually decoded as per the UEFI spec. Add
decoding of the ARM error information value so that the kernel
logs capture all of the valid information at first glance.

ARM error information value decoding is captured in UEFI 2.7
spec tables 263-265.

Signed-off-by: Tyler Baicar <tbai...@codeaurora.org>
---
 drivers/firmware/efi/cper.c | 213 +++++++++++++++++++++++++++++++++++++++++++-
 include/linux/cper.h        |  44 +++++++++
 2 files changed, 255 insertions(+), 2 deletions(-)

diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c
index d2fcafc..0a4ce9d 100644
--- a/drivers/firmware/efi/cper.c
+++ b/drivers/firmware/efi/cper.c
@@ -201,13 +201,218 @@ static void cper_print_proc_generic(const char *pfx,
        "Misc. system register structure",
 };
 
+static const char * const arm_err_trans_type_strs[] = {
+       "Instruction",
+       "Data Access",
+       "Generic",
+};
+
+static const char * const arm_bus_err_op_strs[] = {
+       "Generic error (type cannot be determined)",
+       "Generic read (type of instruction or data request cannot be 
determined)",
+       "Generic write (type of instruction of data request cannot be 
determined)",
+       "Data read",
+       "Data write",
+       "Instruction fetch",
+       "Prefetch",
+};
+
+static const char * const arm_cache_err_op_strs[] = {
+       "Generic error (type cannot be determined)",
+       "Generic read (type of instruction or data request cannot be 
determined)",
+       "Generic write (type of instruction of data request cannot be 
determined)",
+       "Data read",
+       "Data write",
+       "Instruction fetch",
+       "Prefetch",
+       "Eviction",
+       "Snooping (processor initiated a cache snoop that resulted in an 
error)",
+       "Snooped (processor raised a cache error caused by another processor or 
device snooping its cache)",
+       "Management",
+};
+
+static const char * const arm_tlb_err_op_strs[] = {
+       "Generic error (type cannot be determined)",
+       "Generic read (type of instruction or data request cannot be 
determined)",
+       "Generic write (type of instruction of data request cannot be 
determined)",
+       "Data read",
+       "Data write",
+       "Instruction fetch",
+       "Prefetch",
+       "Local management operation (processor initiated a TLB management 
operation that resulted in an error)",
+       "External management operation (processor raised a TLB error caused by 
another processor or device broadcasting TLB operations)",
+};
+
+static const char * const arm_bus_err_part_type_strs[] = {
+       "Local processor originated request",
+       "Local processor responded to request",
+       "Local processor observed",
+       "Generic",
+};
+
+static const char * const arm_bus_err_addr_space_strs[] = {
+       "External Memory Access",
+       "Internal Memory Access",
+       "Unknown",
+       "Device Memory Access",
+};
+
+static void cper_print_arm_err_info(const char *pfx, u32 type,
+                                   u64 error_info)
+{
+       u8 trans_type, op_type, level, participation_type, address_space;
+       u16 mem_attributes;
+       bool proc_context_corrupt, corrected, precise_pc, restartable_pc;
+       bool time_out, access_mode;
+
+       /* If the type is unknown, bail. */
+       if (type > CPER_ARM_MAX_TYPE)
+               return;
+
+       /*
+        * Vendor type errors have error information values that are vendor
+        * specific.
+        */
+       if (type == CPER_ARM_VENDOR_ERROR)
+               return;
+
+       if (error_info & CPER_ARM_ERR_VALID_TRANSACTION_TYPE) {
+               trans_type = ((error_info >> CPER_ARM_ERR_TRANSACTION_SHIFT)
+                             & CPER_ARM_ERR_TRANSACTION_MASK);
+               if (trans_type < ARRAY_SIZE(arm_err_trans_type_strs)) {
+                       printk("%stransaction type: %s\n", pfx,
+                              arm_err_trans_type_strs[trans_type]);
+               }
+       }
+
+       if (error_info & CPER_ARM_ERR_VALID_OPERATION_TYPE) {
+               op_type = ((error_info >> CPER_ARM_ERR_OPERATION_SHIFT)
+                          & CPER_ARM_ERR_OPERATION_MASK);
+               switch (type) {
+               case CPER_ARM_CACHE_ERROR:
+                       if (op_type < ARRAY_SIZE(arm_cache_err_op_strs)) {
+                               printk("%soperation type: %s\n", pfx,
+                                      arm_cache_err_op_strs[op_type]);
+                       }
+                       break;
+               case CPER_ARM_TLB_ERROR:
+                       if (op_type < ARRAY_SIZE(arm_tlb_err_op_strs)) {
+                               printk("%soperation type: %s\n", pfx,
+                                      arm_tlb_err_op_strs[op_type]);
+                       }
+                       break;
+               case CPER_ARM_BUS_ERROR:
+                       if (op_type < ARRAY_SIZE(arm_bus_err_op_strs)) {
+                               printk("%soperation type: %s\n", pfx,
+                                      arm_bus_err_op_strs[op_type]);
+                       }
+                       break;
+               }
+       }
+
+       if (error_info & CPER_ARM_ERR_VALID_LEVEL) {
+               level = ((error_info >> CPER_ARM_ERR_LEVEL_SHIFT)
+                        & CPER_ARM_ERR_LEVEL_MASK);
+               switch (type) {
+               case CPER_ARM_CACHE_ERROR:
+                       printk("%scache level: %d\n", pfx, level);
+                       break;
+               case CPER_ARM_TLB_ERROR:
+                       printk("%sTLB level: %d\n", pfx, level);
+                       break;
+               case CPER_ARM_BUS_ERROR:
+                       printk("%saffinity level at which the bus error 
occurred: %d\n",
+                              pfx, level);
+                       break;
+               }
+       }
+
+       if (error_info & CPER_ARM_ERR_VALID_PROC_CONTEXT_CORRUPT) {
+               proc_context_corrupt = ((error_info >> 
CPER_ARM_ERR_PC_CORRUPT_SHIFT)
+                                       & CPER_ARM_ERR_PC_CORRUPT_MASK);
+               if (proc_context_corrupt)
+                       printk("%sprocessor context corrupted\n", pfx);
+               else
+                       printk("%sprocessor context not corrupted\n", pfx);
+       }
+
+       if (error_info & CPER_ARM_ERR_VALID_CORRECTED) {
+               corrected = ((error_info >> CPER_ARM_ERR_CORRECTED_SHIFT)
+                            & CPER_ARM_ERR_CORRECTED_MASK);
+               if (corrected)
+                       printk("%sthe error has been corrected\n", pfx);
+               else
+                       printk("%sthe error has not been corrected\n", pfx);
+       }
+
+       if (error_info & CPER_ARM_ERR_VALID_PRECISE_PC) {
+               precise_pc = ((error_info >> CPER_ARM_ERR_PRECISE_PC_SHIFT)
+                             & CPER_ARM_ERR_PRECISE_PC_MASK);
+               if (precise_pc)
+                       printk("%sPC is precise\n", pfx);
+               else
+                       printk("%sPC is imprecise\n", pfx);
+       }
+
+       if (error_info & CPER_ARM_ERR_VALID_RESTARTABLE_PC) {
+               restartable_pc = ((error_info >> 
CPER_ARM_ERR_RESTARTABLE_PC_SHIFT)
+                                 & CPER_ARM_ERR_RESTARTABLE_PC_MASK);
+               if (restartable_pc)
+                       printk("%sProgram execution can be restarted reliably 
at the PC associated with the error.\n", pfx);
+       }
+
+       /* The rest of the fields are specific to bus errors */
+       if (type != CPER_ARM_BUS_ERROR)
+               return;
+
+       if (error_info & CPER_ARM_ERR_VALID_PARTICIPATION_TYPE) {
+               participation_type = ((error_info >> 
CPER_ARM_ERR_PARTICIPATION_TYPE_SHIFT)
+                                     & CPER_ARM_ERR_PARTICIPATION_TYPE_MASK);
+               if (participation_type < 
ARRAY_SIZE(arm_bus_err_part_type_strs)) {
+                       printk("%sparticipation type: %s\n", pfx,
+                              arm_bus_err_part_type_strs[participation_type]);
+               }
+       }
+
+       if (error_info & CPER_ARM_ERR_VALID_TIME_OUT) {
+               time_out = ((error_info >> CPER_ARM_ERR_TIME_OUT_SHIFT)
+                           & CPER_ARM_ERR_TIME_OUT_MASK);
+               if (time_out)
+                       printk("%srequest timed out\n", pfx);
+       }
+
+       if (error_info & CPER_ARM_ERR_VALID_ADDRESS_SPACE) {
+               address_space = ((error_info >> 
CPER_ARM_ERR_ADDRESS_SPACE_SHIFT)
+                                & CPER_ARM_ERR_ADDRESS_SPACE_MASK);
+               if (address_space < ARRAY_SIZE(arm_bus_err_addr_space_strs)) {
+                       printk("%saddress space: %s\n", pfx,
+                              arm_bus_err_addr_space_strs[address_space]);
+               }
+       }
+
+       if (error_info & CPER_ARM_ERR_VALID_MEM_ATTRIBUTES) {
+               mem_attributes = ((error_info >> 
CPER_ARM_ERR_MEM_ATTRIBUTES_SHIFT)
+                                 & CPER_ARM_ERR_MEM_ATTRIBUTES_MASK);
+               printk("%smemory access attributes:0x%x\n", pfx, 
mem_attributes);
+       }
+
+       if (error_info & CPER_ARM_ERR_VALID_ACCESS_MODE) {
+               access_mode = ((error_info >> CPER_ARM_ERR_ACCESS_MODE_SHIFT)
+                              & CPER_ARM_ERR_ACCESS_MODE_MASK);
+               if (access_mode)
+                       printk("%saccess mode: normal\n", pfx);
+               else
+                       printk("%saccess mode: secure\n", pfx);
+       }
+}
+
 static void cper_print_proc_arm(const char *pfx,
                                const struct cper_sec_proc_arm *proc)
 {
        int i, len, max_ctx_type;
        struct cper_arm_err_info *err_info;
        struct cper_arm_ctx_info *ctx_info;
-       char newpfx[64];
+       char newpfx[64], infopfx[64];
 
        printk("%sMIDR: 0x%016llx\n", pfx, proc->midr);
 
@@ -259,9 +464,13 @@ static void cper_print_proc_arm(const char *pfx,
                printk("%serror_type: %d, %s\n", newpfx, err_info->type,
                        err_info->type < ARRAY_SIZE(proc_error_type_strs) ?
                        proc_error_type_strs[err_info->type] : "unknown");
-               if (err_info->validation_bits & CPER_ARM_INFO_VALID_ERR_INFO)
+               if (err_info->validation_bits & CPER_ARM_INFO_VALID_ERR_INFO) {
                        printk("%serror_info: 0x%016llx\n", newpfx,
                               err_info->error_info);
+                       snprintf(infopfx, sizeof(infopfx), "%s%s", newpfx, 
INDENT_SP);
+                       cper_print_arm_err_info(infopfx, err_info->type,
+                                               err_info->error_info);
+               }
                if (err_info->validation_bits & CPER_ARM_INFO_VALID_VIRT_ADDR)
                        printk("%svirtual fault address: 0x%016llx\n",
                                newpfx, err_info->virt_fault_addr);
diff --git a/include/linux/cper.h b/include/linux/cper.h
index 723e952..4f28b65 100644
--- a/include/linux/cper.h
+++ b/include/linux/cper.h
@@ -275,6 +275,50 @@ enum {
 #define CPER_ARM_INFO_FLAGS_PROPAGATED         BIT(2)
 #define CPER_ARM_INFO_FLAGS_OVERFLOW           BIT(3)
 
+#define CPER_ARM_CACHE_ERROR                   0
+#define CPER_ARM_TLB_ERROR                     1
+#define CPER_ARM_BUS_ERROR                     2
+#define CPER_ARM_VENDOR_ERROR                  3
+#define CPER_ARM_MAX_TYPE                      CPER_ARM_VENDOR_ERROR
+
+#define CPER_ARM_ERR_VALID_TRANSACTION_TYPE    0x0001
+#define CPER_ARM_ERR_VALID_OPERATION_TYPE      0x0002
+#define CPER_ARM_ERR_VALID_LEVEL               0x0004
+#define CPER_ARM_ERR_VALID_PROC_CONTEXT_CORRUPT        0x0008
+#define CPER_ARM_ERR_VALID_CORRECTED           0x0010
+#define CPER_ARM_ERR_VALID_PRECISE_PC          0x0020
+#define CPER_ARM_ERR_VALID_RESTARTABLE_PC      0x0040
+#define CPER_ARM_ERR_VALID_PARTICIPATION_TYPE  0x0080
+#define CPER_ARM_ERR_VALID_TIME_OUT            0x0100
+#define CPER_ARM_ERR_VALID_ADDRESS_SPACE       0x0200
+#define CPER_ARM_ERR_VALID_MEM_ATTRIBUTES      0x0400
+#define CPER_ARM_ERR_VALID_ACCESS_MODE         0x0800
+
+#define CPER_ARM_ERR_TRANSACTION_SHIFT         16
+#define CPER_ARM_ERR_TRANSACTION_MASK          0x3
+#define CPER_ARM_ERR_OPERATION_SHIFT           18
+#define CPER_ARM_ERR_OPERATION_MASK            0xf
+#define CPER_ARM_ERR_LEVEL_SHIFT               22
+#define CPER_ARM_ERR_LEVEL_MASK                        0x7
+#define CPER_ARM_ERR_PC_CORRUPT_SHIFT          25
+#define CPER_ARM_ERR_PC_CORRUPT_MASK           0x1
+#define CPER_ARM_ERR_CORRECTED_SHIFT           26
+#define CPER_ARM_ERR_CORRECTED_MASK            0x1
+#define CPER_ARM_ERR_PRECISE_PC_SHIFT          27
+#define CPER_ARM_ERR_PRECISE_PC_MASK           0x1
+#define CPER_ARM_ERR_RESTARTABLE_PC_SHIFT      28
+#define CPER_ARM_ERR_RESTARTABLE_PC_MASK       0x1
+#define CPER_ARM_ERR_PARTICIPATION_TYPE_SHIFT  29
+#define CPER_ARM_ERR_PARTICIPATION_TYPE_MASK   0x3
+#define CPER_ARM_ERR_TIME_OUT_SHIFT            31
+#define CPER_ARM_ERR_TIME_OUT_MASK             0x1
+#define CPER_ARM_ERR_ADDRESS_SPACE_SHIFT       32
+#define CPER_ARM_ERR_ADDRESS_SPACE_MASK                0x3
+#define CPER_ARM_ERR_MEM_ATTRIBUTES_SHIFT      34
+#define CPER_ARM_ERR_MEM_ATTRIBUTES_MASK       0x1ff
+#define CPER_ARM_ERR_ACCESS_MODE_SHIFT         43
+#define CPER_ARM_ERR_ACCESS_MODE_MASK          0x1
+
 /*
  * All tables and structs must be byte-packed to match CPER
  * specification, since the tables are provided by the system BIOS
-- 
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm 
Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project.

--
To unsubscribe from this list: send the line "unsubscribe linux-efi" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to