1# Copyright (C) 2019-2022 Intel Corporation.
2#
3# SPDX-License-Identifier: BSD-3-Clause
4#
5
6import parser_lib, os, re
7from inspectorlib import external_tools
8from extractors.helpers import get_bdf_from_realpath
9
10MEM_PATH = ['/proc/iomem', '/proc/meminfo']
11TTY_PATH = '/sys/class/tty/'
12SYS_IRQ_PATH = '/proc/interrupts'
13CPU_INFO_PATH = '/sys/devices/system/cpu/possible'
14
15# Please refer kernel_src/include/linux/serial_core.h
16ttys_type = {
17    '0': 'PORT', # 8b I/O port access
18    '2': 'MMIO', # driver-specific
19    '3': 'MMIO', # 32b little endian
20    '6': 'MMIO', # 32b big endian
21}
22
23
24ttys_irqs = []
25
26
27def read_ttys_node(path):
28        with open(path, 'rt') as info:
29            ret_value = info.readline().strip()
30
31        return ret_value
32
33
34def detected_ttys():
35    ttys_cnt = 8
36    tty_used_list = []
37    for s_inc in range(ttys_cnt):
38        cmd = 'stty -F /dev/ttyS{}'.format(s_inc)
39        res = external_tools.run('{}'.format(cmd))
40
41        while True:
42            line = res.stdout.readline().decode('ascii')
43
44            line_len = len(line.split())
45            if not line_len or line.split()[-1] == 'error':
46                break
47
48            ttys_n = "/dev/ttyS{}".format(s_inc)
49            tty_used_list.append(ttys_n)
50            break
51
52    return tty_used_list
53
54
55def is_bdf_format(bdf):
56    is_bdf = False
57
58    if len(bdf) == 7 and len(bdf.split(':')[0]) == 2 and len(bdf.split(':')[1].split('.')[0]) == 2 and len(bdf.split(".")[1]) == 1:
59        is_bdf = True
60
61    return is_bdf
62
63
64def iomem2bdf(base):
65    bdf = ''
66    base_iomem = base.strip('0x').lower()
67    with open(MEM_PATH[0], 'rt') as mem_info:
68
69        while True:
70            line = mem_info.readline().strip('\n')
71            if not line:
72                break
73
74            if base_iomem in line and '0000:' in line:
75                bdf = line.split(" ")[-1].lstrip("0000").lstrip(":")
76                if not is_bdf_format(bdf):
77                    continue
78                else:
79                    break
80
81    return bdf
82
83def dump_ttys_info(ttys_list, config):
84    for ttys in ttys_list:
85        ttys_n = ttys.split('/')[-1]
86        type_path = '{}{}/io_type'.format(TTY_PATH, ttys_n)
87        serial_type = read_ttys_node(type_path)
88
89        irq_path = '{}{}/irq'.format(TTY_PATH, ttys_n)
90        irq = read_ttys_node(irq_path)
91        ttys_irqs.append(irq)
92
93        if ttys_type[serial_type] == 'PORT':
94            base_path = '{}{}/port'.format(TTY_PATH, ttys_n)
95            base = read_ttys_node(base_path)
96            try:
97                b = get_bdf_from_realpath(os.path.join(TTY_PATH, ttys_n, 'device'))
98                bdf = f'{b[0]}:{b[1]}.{b[2]}'
99            except AssertionError:
100                bdf = ''
101            if bdf:
102                print("\tseri:{} type:portio base:{} irq:{} bdf:{}".format(ttys, base, irq, bdf), file=config)
103            else:
104                print("\tseri:{} type:portio base:{} irq:{}".format(ttys, base, irq), file=config)
105        elif ttys_type[serial_type] == 'MMIO':
106            base_path = '{}{}/iomem_base'.format(TTY_PATH, ttys_n)
107            base = read_ttys_node(base_path)
108            bdf = iomem2bdf(base)
109            if bdf:
110                print('\tseri:{} type:mmio base:{} irq:{} bdf:"{}"'.format(ttys, base, irq, bdf), file=config)
111            else:
112                print('\tseri:{} type:mmio base:{} irq:{}'.format(ttys, base, irq), file=config)
113
114
115def dump_ttys(config):
116    """This will get systemd ram which are usable
117    :param config: file pointer that opened for writing board config information
118    """
119    print("\t<TTYS_INFO>", file=config)
120    ttys_list = detected_ttys()
121
122    dump_ttys_info(ttys_list, config)
123
124    print("\t</TTYS_INFO>", file=config)
125    print("", file=config)
126
127
128def dump_free_irqs(config):
129    irq_list = ['3', '4', '5', '6', '7', '8', '10', '11', '12', '13', '14', '15']
130    for tty_irq in ttys_irqs:
131        if tty_irq in irq_list:
132            irq_list.remove(tty_irq)
133
134    print("\t<AVAILABLE_IRQ_INFO>", file=config)
135    with open(SYS_IRQ_PATH, 'rt') as irq_config:
136
137        while True:
138            line = irq_config.readline().strip()
139            if ':' not in line:
140                continue
141
142            irq_num = line.split(':')[0]
143            if not line or int(irq_num) >= 16:
144                break
145
146            if irq_num in irq_list:
147                irq_list.remove(irq_num)
148
149    i_cnt = 0
150    print("\t", end="", file=config)
151    for irq in irq_list:
152        i_cnt += 1
153
154        if i_cnt == len(irq_list):
155            print("{}".format(irq), file=config)
156        else:
157            print("{}, ".format(irq), end="", file=config)
158
159    print("\t</AVAILABLE_IRQ_INFO>", file=config)
160    print("", file=config)
161
162
163def dump_system_ram(config):
164    """This will get systemd ram which are usable
165    :param config: file pointer that opened for writing board config information
166    """
167    print("\t<IOMEM_INFO>", file=config)
168    with open(MEM_PATH[0], 'rt', errors='ignore') as mem_info:
169
170        while True:
171            line = mem_info.readline().strip('\n')
172            line = re.sub('[^!-~]+', ' ', line)
173
174            if not line:
175                break
176
177            print("\t{}".format(line), file=config)
178
179    print("\t</IOMEM_INFO>", file=config)
180    print("", file=config)
181
182
183def dump_block_dev(config):
184    """This will get available block device
185    :param config: file pointer that opened for writing board config information
186    """
187    cmd = 'blkid'
188    desc = 'BLOCK_DEVICE_INFO'
189    parser_lib.dump_execute(cmd, desc, config)
190    print("", file=config)
191
192
193
194def dump_total_mem(config):
195
196    total_mem = 0
197    print("\t<TOTAL_MEM_INFO>", file=config)
198    with open(MEM_PATH[1], 'rt') as mem_info:
199        while True:
200            line = mem_info.readline().strip()
201
202            if not line:
203                break
204
205            if ':' in line and line.split(':')[0].strip() == "MemTotal":
206                total_mem = line.split(':')[1]
207                print("\t{}".format(total_mem.strip()), file=config)
208
209    print("\t</TOTAL_MEM_INFO>", file=config)
210    print("", file=config)
211
212
213def dump_cpu_core_info(config):
214
215    print("\t<CPU_PROCESSOR_INFO>", file=config)
216    with open(CPU_INFO_PATH, 'rt') as cpu_info:
217        line = cpu_info.readline()
218
219        processor_id = int(line.split('-')[0].strip())
220        print("\t{}".format(processor_id), end="", file=config)
221
222        processor_id += 1
223        while (processor_id <= int(line.split('-')[1].strip())):
224            print(", {}".format(processor_id), end="", file=config)
225            processor_id += 1
226
227    print("", file=config)
228    print("\t</CPU_PROCESSOR_INFO>", file=config)
229    print("", file=config)
230
231def dump_max_msix_table_num(config):
232
233    msix_table_num_list = []
234    max_msix_table_num = 1
235    cmd = 'lspci -vv | grep "MSI-X" | grep "Count="'
236    res_lines = parser_lib.get_output_lines(cmd)
237    for line in res_lines:
238        tmp_num = line.split('=')[1].split()[0]
239        msix_table_num_list.append(int(tmp_num))
240
241    if msix_table_num_list:
242        max_msix_table_num = max(msix_table_num_list)
243    print("\t<MAX_MSIX_TABLE_NUM>", file=config)
244    print("\t{}".format(max_msix_table_num), file=config)
245    print("\t</MAX_MSIX_TABLE_NUM>", file=config)
246
247
248def dump_dev_config_info(config):
249
250    dump_max_msix_table_num(config)
251    print("", file=config)
252
253def generate_info(board_info):
254    """Get System Ram information
255    :param board_info: this is the file which stores the hardware board information
256    """
257    with open(board_info, 'a+') as config:
258
259        dump_system_ram(config)
260
261        dump_block_dev(config)
262
263        dump_ttys(config)
264
265        dump_free_irqs(config)
266
267        dump_total_mem(config)
268
269        dump_cpu_core_info(config)
270
271        dump_dev_config_info(config)
272