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}
57DEFAULT_CHIP = 'npcx7m6'
58
59# RAM related values
60RAM_ADDR = 0x00
61RAM_SIZE = 0x01
62
63class EcstArgs:
64    """creates an arguments object for the ECST,
65    the arguments are taken from the command line and/or
66    argument file
67    """
68    error_args = None
69
70    mode = DEFAULT_MODE
71    help = False
72    verbose = DEFAULT_VERBOSE
73    super_verbose = False
74    input = None
75    output = None
76    args_file = None
77    chip_name = DEFAULT_CHIP
78    chip_ram_address = CHIPS_INFO[DEFAULT_CHIP]['ram_address']
79    chip_ram_size = CHIPS_INFO[DEFAULT_CHIP]['ram_address']
80    firmware_header_crc = FW_HDR_CRC_DEFAULT
81    firmware_crc = FW_CRC_DEFAULT
82    spi_flash_maximum_clock = SPI_MAX_CLOCK_DEFAULT
83    spi_flash_clock_ratio = SPI_CLOCK_RATIO_DEFAULT
84    unlimited_burst_mode = SPI_UNLIMITED_BURST_DEFAULT
85    spi_flash_read_mode = SPI_MODE_VAL_DEFAULT
86    firmware_load_address = None
87    firmware_entry_point = None
88    use_arm_reset = True
89    firmware_crc_start = FW_CRC_START_OFFSET_DEFAULT
90    firmware_crc_size = None
91    firmware_length = None
92    flash_size = FLASH_SIZE_DEFAULT
93    paste_firmware_header = PASTE_FIRMWARE_HEADER_DEFAULT
94    pointer = POINTER_OFFSET_DEFAULT
95    bh_offset = None
96
97    def __init__(self):
98
99        arguments = _create_parser("")
100        valid_arguments = arguments[0]
101        invalid_arguments = arguments[1]
102        self.error_args = invalid_arguments
103
104        _populate_args(self, valid_arguments)
105        _populate_chip_fields(self)
106
107def _populate_chip_fields(self):
108    """populate the chip related fields for the ecst"""
109    self.chip_name = self.chip_name
110    chip = str(self.chip_name).lower()
111
112    if chip not in CHIPS_INFO:
113        self.chip_name = INVALID_INPUT
114        return
115
116    self.chip_ram_address = CHIPS_INFO[chip]['ram_address']
117    self.chip_ram_size = CHIPS_INFO[chip]['ram_size']
118    if self.firmware_load_address is None:
119        self.firmware_load_address = self.chip_ram_address
120
121def _populate_args(self, argument_list):
122    """populate the ecst arguments according to the command line/ args file"""
123    for arg in vars(argument_list):
124        if (arg == "input") & (argument_list.input is not None):
125            self.input = argument_list.input
126
127        elif (arg == "output") & (argument_list.output is not None):
128            self.output = argument_list.output
129
130        elif (arg == "chip") & (argument_list.chip is not None):
131            self.chip_name = argument_list.chip
132            _populate_chip_fields(self)
133
134        elif (arg == "verbose") & argument_list.verbose:
135            self.verbose = REG_VERBOSE
136
137        elif (arg == "super_verbose") & argument_list.super_verbose:
138            self.verbose = SUPER_VERBOSE
139
140        elif (arg == "spi_flash_maximum_clock") & \
141                (argument_list.spi_flash_maximum_clock is not None):
142            self.spi_flash_maximum_clock =\
143                argument_list.spi_flash_maximum_clock
144
145        elif (arg == "spi_flash_clock_ratio") & \
146                (argument_list.spi_flash_clock_ratio is not None):
147            if argument_list.spi_flash_clock_ratio.isdigit():
148                self.spi_flash_clock_ratio =\
149                    int(argument_list.spi_flash_clock_ratio)
150            else:
151                self.spi_flash_clock_ratio = INVALID_INPUT
152
153        elif (arg == "firmware_header_crc") &\
154                argument_list.firmware_header_crc:
155            self.firmware_header_crc = FW_HDR_CRC_DISABLE
156
157        elif (arg == "firmware_crc") & argument_list.firmware_crc:
158            self.firmware_crc = FW_CRC_DISABLE
159
160        elif (arg == "spi_read_mode") &\
161                (argument_list.spi_read_mode is not None):
162
163            self.spi_flash_read_mode = argument_list.spi_read_mode
164
165        elif (arg == "flash_size") & (argument_list.flash_size is not None):
166            self.flash_size = argument_list.flash_size
167
168        elif (arg == "paste_firmware_header") & \
169                (argument_list.paste_firmware_header is not None):
170            if _is_hex(argument_list.paste_firmware_header):
171                self.paste_firmware_header =\
172                    int(argument_list.paste_firmware_header, 16)
173            else:
174                self.paste_firmware_header = INVALID_INPUT
175
176def _create_parser(arg_list):
177    """create argument parser according to pre-defined arguments
178
179    :param arg_list: when empty, parses command line arguments,
180    else parses the given string
181    """
182
183    parser = argparse.ArgumentParser(conflict_handler='resolve', allow_abbrev=False)
184    parser.add_argument("-i", nargs='?', dest="input")
185    parser.add_argument("-o", nargs='?', dest="output")
186    parser.add_argument("-chip", dest="chip")
187    parser.add_argument("-v", action="store_true", dest="verbose")
188    parser.add_argument("-vv", action="store_true", dest="super_verbose")
189    parser.add_argument("-nohcrc", action="store_true",
190                        dest="firmware_header_crc")
191    parser.add_argument("-nofcrc", action="store_true", dest="firmware_crc")
192    parser.add_argument("-spimaxclk", nargs='?',
193                        dest="spi_flash_maximum_clock")
194    parser.add_argument("-spiclkratio", nargs='?',
195                        dest="spi_flash_clock_ratio")
196    parser.add_argument("-spireadmode", nargs='?', dest="spi_read_mode")
197    parser.add_argument("-flashsize", nargs='?', dest="flash_size")
198    parser.add_argument("-ph", nargs='?', dest="paste_firmware_header")
199
200    args = parser.parse_known_args(arg_list.split())
201
202    if arg_list == "":
203        args = parser.parse_known_args()
204
205    return args
206
207def _file_to_line(arg_file):
208    """helper to convert a text file to one line string
209    used to parse the arguments in a given argfile
210
211    :param arg_file: the file to manipulate
212    """
213    with open(arg_file, "r") as arg_file_to_read:
214        data = arg_file_to_read.read().strip()
215    arg_file_to_read.close()
216
217    return data
218
219def _is_hex(val):
220    """helper to determine whether an input is a hex
221    formatted number
222
223    :param val: input to be checked
224    """
225    if val.startswith("0x") or val.startswith("0X"):
226        val = val[2:]
227    hex_digits = set("0123456789abcdefABCDEF")
228    for char in val:
229        if char not in hex_digits:
230            return False
231    return True
232
233def exit_with_failure(message):
234    """formatted failure message printer, prints the
235    relevant error message and exits the application.
236
237    :param message: the error message to be printed
238    """
239
240    message = '\n' + message
241    message += '\n'
242    message += '******************************\n'
243    message += '***        FAILED          ***\n'
244    message += '******************************\n'
245    print(Fore.RED + message)
246
247    sys.exit(EXIT_FAILURE_STATUS)
248
249colorama.init()
250