1#!/usr/bin/env python3
2#
3# Copyright (c) 2020 Nuvoton Technology Corporation
4#
5# SPDX-License-Identifier: Apache-2.0
6
7# This file contains general functions for ECST application
8
9import sys
10import argparse
11import colorama
12from colorama import Fore
13
14INVALID_INPUT = -1
15EXIT_FAILURE_STATUS = 1
16
17# Header common fields
18FW_HDR_CRC_DISABLE = 0x00
19FW_HDR_CRC_ENABLE = 0x02
20FW_CRC_DISABLE = 0x00
21FW_CRC_ENABLE = 0x02
22
23SPI_CLOCK_RATIO_1 = 0x00
24SPI_CLOCK_RATIO_2 = 0x08
25
26SPI_UNLIMITED_BURST_DISABLE = 0x00
27SPI_UNLIMITED_BURST_ENABLE = 0x08
28
29# Verbose related values
30NO_VERBOSE = 0
31REG_VERBOSE = 1
32SUPER_VERBOSE = 1
33
34# argument default values.
35DEFAULT_MODE = "bt"
36SPI_MAX_CLOCK_DEFAULT = "20"
37FLASH_SIZE_DEFAULT = "16"
38SPI_CLOCK_RATIO_DEFAULT = 1
39PASTE_FIRMWARE_HEADER_DEFAULT = 0x00000000
40DEFAULT_VERBOSE = NO_VERBOSE
41SPI_MODE_VAL_DEFAULT = 'normal'
42SPI_UNLIMITED_BURST_DEFAULT = SPI_UNLIMITED_BURST_DISABLE
43FW_HDR_CRC_DEFAULT = FW_HDR_CRC_ENABLE
44FW_CRC_DEFAULT = FW_CRC_ENABLE
45FW_CRC_START_OFFSET_DEFAULT = 0x0
46POINTER_OFFSET_DEFAULT = 0x0
47
48# Chips: convert from name to index.
49CHIPS_INFO = {
50    'npcx7m5': {'ram_address': 0x100a8000, 'ram_size': 0x20000},
51    'npcx7m6': {'ram_address': 0x10090000, 'ram_size': 0x40000},
52    'npcx7m7': {'ram_address': 0x10070000, 'ram_size': 0x60000},
53    'npcx9m3': {'ram_address': 0x10080000, 'ram_size': 0x50000},
54    'npcx9m6': {'ram_address': 0x10090000, 'ram_size': 0x40000},
55    'npcx9m7': {'ram_address': 0x10070000, 'ram_size': 0x60000},
56    'npcx9mfp': {'ram_address': 0x10058000, 'ram_size': 0x80000},
57    'npcx4m3': {'ram_address': 0x10088000, 'ram_size': 0x50000},
58    'npcx4m8': {'ram_address': 0x10060000, 'ram_size': 0x7c800},
59}
60DEFAULT_CHIP = 'npcx7m6'
61
62# RAM related values
63RAM_ADDR = 0x00
64RAM_SIZE = 0x01
65
66class EcstArgs:
67    """creates an arguments object for the ECST,
68    the arguments are taken from the command line and/or
69    argument file
70    """
71    error_args = None
72
73    mode = DEFAULT_MODE
74    help = False
75    verbose = DEFAULT_VERBOSE
76    super_verbose = False
77    input = None
78    output = None
79    args_file = None
80    chip_name = DEFAULT_CHIP
81    chip_ram_address = CHIPS_INFO[DEFAULT_CHIP]['ram_address']
82    chip_ram_size = CHIPS_INFO[DEFAULT_CHIP]['ram_address']
83    firmware_header_crc = FW_HDR_CRC_DEFAULT
84    firmware_crc = FW_CRC_DEFAULT
85    spi_flash_maximum_clock = SPI_MAX_CLOCK_DEFAULT
86    spi_flash_clock_ratio = SPI_CLOCK_RATIO_DEFAULT
87    unlimited_burst_mode = SPI_UNLIMITED_BURST_DEFAULT
88    spi_flash_read_mode = SPI_MODE_VAL_DEFAULT
89    firmware_load_address = None
90    firmware_entry_point = None
91    use_arm_reset = True
92    firmware_crc_start = FW_CRC_START_OFFSET_DEFAULT
93    firmware_crc_size = None
94    firmware_length = None
95    flash_size = FLASH_SIZE_DEFAULT
96    paste_firmware_header = PASTE_FIRMWARE_HEADER_DEFAULT
97    pointer = POINTER_OFFSET_DEFAULT
98    bh_offset = None
99
100    def __init__(self):
101
102        arguments = _create_parser("")
103        valid_arguments = arguments[0]
104        invalid_arguments = arguments[1]
105        self.error_args = invalid_arguments
106
107        _populate_args(self, valid_arguments)
108        _populate_chip_fields(self)
109
110def _populate_chip_fields(self):
111    """populate the chip related fields for the ecst"""
112    self.chip_name = self.chip_name
113    chip = str(self.chip_name).lower()
114
115    if chip not in CHIPS_INFO:
116        self.chip_name = INVALID_INPUT
117        return
118
119    self.chip_ram_address = CHIPS_INFO[chip]['ram_address']
120    self.chip_ram_size = CHIPS_INFO[chip]['ram_size']
121    if self.firmware_load_address is None:
122        self.firmware_load_address = self.chip_ram_address
123
124def _populate_args(self, argument_list):
125    """populate the ecst arguments according to the command line/ args file"""
126    for arg in vars(argument_list):
127        if (arg == "input") & (argument_list.input is not None):
128            self.input = argument_list.input
129
130        elif (arg == "output") & (argument_list.output is not None):
131            self.output = argument_list.output
132
133        elif (arg == "chip") & (argument_list.chip is not None):
134            self.chip_name = argument_list.chip
135            _populate_chip_fields(self)
136
137        elif (arg == "verbose") & argument_list.verbose:
138            self.verbose = REG_VERBOSE
139
140        elif (arg == "super_verbose") & argument_list.super_verbose:
141            self.verbose = SUPER_VERBOSE
142
143        elif (arg == "spi_flash_maximum_clock") & \
144                (argument_list.spi_flash_maximum_clock is not None):
145            self.spi_flash_maximum_clock =\
146                argument_list.spi_flash_maximum_clock
147
148        elif (arg == "spi_flash_clock_ratio") & \
149                (argument_list.spi_flash_clock_ratio is not None):
150            if argument_list.spi_flash_clock_ratio.isdigit():
151                self.spi_flash_clock_ratio =\
152                    int(argument_list.spi_flash_clock_ratio)
153            else:
154                self.spi_flash_clock_ratio = INVALID_INPUT
155
156        elif (arg == "firmware_header_crc") &\
157                argument_list.firmware_header_crc:
158            self.firmware_header_crc = FW_HDR_CRC_DISABLE
159
160        elif (arg == "firmware_crc") & argument_list.firmware_crc:
161            self.firmware_crc = FW_CRC_DISABLE
162
163        elif (arg == "spi_read_mode") &\
164                (argument_list.spi_read_mode is not None):
165
166            self.spi_flash_read_mode = argument_list.spi_read_mode
167
168        elif (arg == "flash_size") & (argument_list.flash_size is not None):
169            self.flash_size = argument_list.flash_size
170
171        elif (arg == "paste_firmware_header") & \
172                (argument_list.paste_firmware_header is not None):
173            if _is_hex(argument_list.paste_firmware_header):
174                self.paste_firmware_header =\
175                    int(argument_list.paste_firmware_header, 16)
176            else:
177                self.paste_firmware_header = INVALID_INPUT
178
179def _create_parser(arg_list):
180    """create argument parser according to pre-defined arguments
181
182    :param arg_list: when empty, parses command line arguments,
183    else parses the given string
184    """
185
186    parser = argparse.ArgumentParser(conflict_handler='resolve', allow_abbrev=False)
187    parser.add_argument("-i", nargs='?', dest="input")
188    parser.add_argument("-o", nargs='?', dest="output")
189    parser.add_argument("-chip", dest="chip")
190    parser.add_argument("-v", action="store_true", dest="verbose")
191    parser.add_argument("-vv", action="store_true", dest="super_verbose")
192    parser.add_argument("-nohcrc", action="store_true",
193                        dest="firmware_header_crc")
194    parser.add_argument("-nofcrc", action="store_true", dest="firmware_crc")
195    parser.add_argument("-spimaxclk", nargs='?',
196                        dest="spi_flash_maximum_clock")
197    parser.add_argument("-spiclkratio", nargs='?',
198                        dest="spi_flash_clock_ratio")
199    parser.add_argument("-spireadmode", nargs='?', dest="spi_read_mode")
200    parser.add_argument("-flashsize", nargs='?', dest="flash_size")
201    parser.add_argument("-ph", nargs='?', dest="paste_firmware_header")
202
203    args = parser.parse_known_args(arg_list.split())
204
205    if arg_list == "":
206        args = parser.parse_known_args()
207
208    return args
209
210def _file_to_line(arg_file):
211    """helper to convert a text file to one line string
212    used to parse the arguments in a given argfile
213
214    :param arg_file: the file to manipulate
215    """
216    with open(arg_file, "r") as arg_file_to_read:
217        data = arg_file_to_read.read().strip()
218    arg_file_to_read.close()
219
220    return data
221
222def _is_hex(val):
223    """helper to determine whether an input is a hex
224    formatted number
225
226    :param val: input to be checked
227    """
228    if val.startswith("0x") or val.startswith("0X"):
229        val = val[2:]
230    hex_digits = set("0123456789abcdefABCDEF")
231    for char in val:
232        if char not in hex_digits:
233            return False
234    return True
235
236def exit_with_failure(message):
237    """formatted failure message printer, prints the
238    relevant error message and exits the application.
239
240    :param message: the error message to be printed
241    """
242
243    message = '\n' + message
244    message += '\n'
245    message += '******************************\n'
246    message += '***        FAILED          ***\n'
247    message += '******************************\n'
248    print(Fore.RED + message)
249
250    sys.exit(EXIT_FAILURE_STATUS)
251
252colorama.init()
253