1#!/usr/bin/env python3
2#
3# Copyright (c) 2020 Intel Corporation
4#
5# SPDX-License-Identifier: Apache-2.0
6
7import logging
8import struct
9
10
11# Note: keep sync with C code
12COREDUMP_HDR_ID = b'ZE'
13COREDUMP_HDR_VER = 1
14LOG_HDR_STRUCT = "<ccHHBBI"
15LOG_HDR_SIZE = struct.calcsize(LOG_HDR_STRUCT)
16
17COREDUMP_ARCH_HDR_ID = b'A'
18LOG_ARCH_HDR_STRUCT = "<cHH"
19LOG_ARCH_HDR_SIZE = struct.calcsize(LOG_ARCH_HDR_STRUCT)
20
21COREDUMP_MEM_HDR_ID = b'M'
22COREDUMP_MEM_HDR_VER = 1
23LOG_MEM_HDR_STRUCT = "<cH"
24LOG_MEM_HDR_SIZE = struct.calcsize(LOG_MEM_HDR_STRUCT)
25
26
27logger = logging.getLogger("parser")
28
29
30def reason_string(reason):
31    # Keep sync with "enum k_fatal_error_reason"
32    ret = "(Unknown)"
33
34    if reason == 0:
35        ret = "K_ERR_CPU_EXCEPTION"
36    elif reason == 1:
37        ret = "K_ERR_SPURIOUS_IRQ"
38    elif reason == 2:
39        ret = "K_ERR_STACK_CHK_FAIL"
40    elif reason == 3:
41        ret = "K_ERR_KERNEL_OOPS"
42    elif reason == 4:
43        ret = "K_ERR_KERNEL_PANIC"
44
45    return ret
46
47
48class CoredumpLogFile:
49    """
50    Process the binary coredump file for register block
51    and memory blocks.
52    """
53
54    def __init__(self, logfile):
55        self.logfile = logfile
56        self.fd = None
57
58        self.log_hdr = None
59        self.arch_data = list()
60        self.memory_regions = list()
61
62    def open(self):
63        self.fd = open(self.logfile, "rb")
64
65    def close(self):
66        self.fd.close()
67
68    def get_arch_data(self):
69        return self.arch_data
70
71    def get_memory_regions(self):
72        return self.memory_regions
73
74    def parse_arch_section(self):
75        hdr = self.fd.read(LOG_ARCH_HDR_SIZE)
76        _, hdr_ver, num_bytes = struct.unpack(LOG_ARCH_HDR_STRUCT, hdr)
77
78        arch_data = self.fd.read(num_bytes)
79
80        self.arch_data = {"hdr_ver" : hdr_ver, "data" : arch_data}
81
82        return True
83
84    def parse_memory_section(self):
85        hdr = self.fd.read(LOG_MEM_HDR_SIZE)
86        _, hdr_ver = struct.unpack(LOG_MEM_HDR_STRUCT, hdr)
87
88        if hdr_ver != COREDUMP_MEM_HDR_VER:
89            logger.error(f"Memory block version: {hdr_ver}, expected {COREDUMP_MEM_HDR_VER}!")
90            return False
91
92        # Figure out how to read the start and end addresses
93        ptr_fmt = None
94        if self.log_hdr["ptr_size"] == 64:
95            ptr_fmt = "QQ"
96        elif self.log_hdr["ptr_size"] == 32:
97            ptr_fmt = "II"
98        else:
99            return False
100
101        data = self.fd.read(struct.calcsize(ptr_fmt))
102        saddr, eaddr = struct.unpack(ptr_fmt, data)
103
104        size = eaddr - saddr
105
106        data = self.fd.read(size)
107
108        mem = {"start": saddr, "end": eaddr, "data": data}
109        self.memory_regions.append(mem)
110
111        logger.info("Memory: 0x%x to 0x%x of size %d" %
112                    (saddr, eaddr, size))
113
114        return True
115
116    def parse(self):
117        if self.fd is None:
118            self.open()
119
120        hdr = self.fd.read(LOG_HDR_SIZE)
121        id1, id2, hdr_ver, tgt_code, ptr_size, flags, reason = struct.unpack(LOG_HDR_STRUCT, hdr)
122
123        if (id1 + id2) != COREDUMP_HDR_ID:
124            # ID in header does not match
125            logger.error("Log header ID not found...")
126            return False
127
128        if hdr_ver != COREDUMP_HDR_VER:
129            logger.error(f"Log version: {hdr_ver}, expected: {COREDUMP_HDR_VER}!")
130            return False
131
132        ptr_size = 2 ** ptr_size
133
134        self.log_hdr = {
135                        "hdr_version": hdr_ver,
136                        "tgt_code": tgt_code,
137                        "ptr_size": ptr_size,
138                        "flags": flags,
139                        "reason": reason,
140                        }
141
142        logger.info("Reason: {0}".format(reason_string(reason)))
143        logger.info(f"Pointer size {ptr_size}")
144
145        del id1, id2, hdr_ver, tgt_code, ptr_size, flags, reason
146
147        while True:
148            section_id = self.fd.read(1)
149            if not section_id:
150                # no more data to read
151                break
152
153            self.fd.seek(-1, 1) # go back 1 byte
154            if section_id == COREDUMP_ARCH_HDR_ID:
155                if not self.parse_arch_section():
156                    logger.error("Cannot parse architecture section")
157                    return False
158            elif section_id == COREDUMP_MEM_HDR_ID:
159                if not self.parse_memory_section():
160                    logger.error("Cannot parse memory section")
161                    return False
162            else:
163                # Unknown section in log file
164                logger.error(f"Unknown section in log file with ID {section_id}")
165                return False
166
167        return True
168