-
Notifications
You must be signed in to change notification settings - Fork 24
/
sdphost.py
221 lines (188 loc) · 6.69 KB
/
sdphost.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#
# Copyright 2020-2024 NXP
#
# SPDX-License-Identifier: BSD-3-Clause
"""Console script for SDP module aka SDPHost."""
import inspect
import json
import sys
from typing import Optional
import click
from spsdk.apps.utils import spsdk_logger
from spsdk.apps.utils.common_cli_options import (
CommandsTreeGroup,
spsdk_apps_common_options,
spsdk_output_option,
spsdk_sdp_interface,
spsdk_use_json_option,
)
from spsdk.apps.utils.utils import INT, SPSDKAppError, catch_spsdk_error, format_raw_data
from spsdk.sdp.commands import ResponseValue
from spsdk.sdp.protocol.base import SDPProtocolBase
from spsdk.sdp.sdp import SDP
from spsdk.utils import misc
@click.group(name="sdphost", no_args_is_help=True, cls=CommandsTreeGroup)
@spsdk_sdp_interface()
@spsdk_use_json_option
@spsdk_apps_common_options
@click.pass_context
def main(
ctx: click.Context,
interface: SDPProtocolBase,
use_json: bool,
log_level: int,
) -> int:
"""Utility for communication with ROM on i.MX targets using SDP protocol."""
spsdk_logger.install(level=log_level)
ctx.obj = {
"interface": interface,
"use_json": use_json,
}
return 0
@main.command()
@click.pass_context
def error_status(ctx: click.Context) -> None:
"""Reads the error code from the device."""
with SDP(ctx.obj["interface"]) as sdp:
response = sdp.read_status()
display_output(
[response],
sdp.hab_status,
ctx.obj["use_json"],
extra_output=f"Response status = {decode_status_code(response)}.",
)
@main.command()
@click.argument("address", type=INT(), required=True)
@click.pass_context
def jump_address(ctx: click.Context, address: int) -> None:
"""Jumps to the entry point of the image at the given address.
jump-address will result in the execution of the image once the ROM process
the IVT and on successful authentication of the image.
\b
ADDRESS - starting address of the image
"""
with SDP(ctx.obj["interface"]) as sdp:
sdp.jump_and_run(address)
display_output([], sdp.hab_status)
@main.command()
@click.argument("address", type=INT(), required=True)
@click.argument("bin_file", metavar="FILE", type=click.File("rb"), required=True)
@click.argument("count", type=INT(), required=False)
@click.pass_context
def write_file(ctx: click.Context, address: int, bin_file: click.File, count: int) -> None:
"""Writes file to the device's memory address.
\b
ADDRESS - starting address of the image
FILE - binary file to write
COUNT - Count is the size of data to write in bytes (default: whole file)
"""
data = bin_file.read(count) # type: ignore
with SDP(ctx.obj["interface"]) as sdp:
sdp.write_file(address, data)
display_output(
[],
sdp.hab_status,
extra_output=f"Response status = {decode_status_code(sdp.cmd_status)}.",
)
@main.command()
@click.argument("address", type=INT(), required=True)
@click.argument("item_length", type=INT(), required=False, default="32", metavar="[FORMAT]")
@click.argument("count", type=INT(), required=False, default=None)
@spsdk_output_option(required=False)
@click.option("-h", "--use-hexdump", is_flag=True, default=False, help="Use hexdump format")
@click.pass_context
def read_register(
ctx: click.Context,
address: int,
item_length: int,
count: int,
output: str,
use_hexdump: bool,
) -> None:
"""Reads the contents of a memory location or register value.
The address of the register or memory location should be passed in as the first argument.
Optional arguments include the data format of the register value in the number of bits
and number of bytes to read.
\b
ADDRESS - starting address where to read
FORMAT - bits per item: valid values: 8, 16, 32; default 32
COUNT - bytes to read; default size of FORMAT
"""
with SDP(ctx.obj["interface"]) as sdp:
response = sdp.read_safe(address, count, item_length)
if not response:
raise SPSDKAppError(
f"Error: invalid sub-command or arguments 'read-register {address:#8X} {item_length} {count}'"
)
if output:
misc.write_file(response, output, mode="wb")
click.echo(f"{len(response)} bytes written to {output}")
else:
click.echo(format_raw_data(response, use_hexdump=use_hexdump))
display_output([], sdp.hab_status, ctx.obj["use_json"])
@click.argument("baudrate", type=INT(), required=True)
@main.command()
@click.pass_context
def set_baudrate(ctx: click.Context, baudrate: int) -> None:
"""Configures UART baudrate.
The SDP command SET_BAUDRATE is used by the host to configure the UART
baudrate on the device side. The default baudrate is 115200.
Please note that this command is not supported on all devices.
\b
BAUDRATE - baudrate to be set
"""
with SDP(ctx.obj["interface"]) as sdp:
response = sdp.set_baudrate(baudrate)
display_output(
[response],
sdp.hab_status,
ctx.obj["use_json"],
extra_output=f"Response status = {decode_status_code(response)}.",
)
def display_output(
response: list, status_code: int, use_json: bool = False, extra_output: Optional[str] = None
) -> None:
"""Printout the response.
:param response: Response list to display
:param status_code: Response status
:param use_json: use JSON output format
:param extra_output: Extra string to display
"""
if use_json:
data = {
# get the name of a caller function and replace _ with -
"command": inspect.stack()[1].function.replace("_", "-"),
# this is just a visualization thing
"response": response or [],
"status": {
"description": decode_status_code(status_code),
"value": status_code,
},
}
print(json.dumps(data, indent=3))
else:
print(f"Status (HAB mode) = {decode_status_code(status_code)}.")
if extra_output:
print(extra_output)
def decode_status_code(status_code: Optional[int] = None) -> str:
"""Returns a stringified representation of status code.
:param status_code: SDP status code
:return: stringified representation
"""
if not status_code:
return "UNKNOWN ERROR"
no_desc = "NO DESCRIPTION"
desc = (
ResponseValue.get_description(status_code, no_desc)
if ResponseValue.contains(status_code)
else no_desc
)
return f"{status_code} ({status_code:#x}) {desc}"
@catch_spsdk_error
def safe_main() -> None:
"""Calls the main function."""
sys.exit(main()) # pylint: disable=no-value-for-parameter
if __name__ == "__main__":
safe_main()