1
1
import asyncio
2
+ import binascii
2
3
import logging
3
4
import os
4
5
import shlex
5
6
import socket
6
7
import typing as t
8
+ from dataclasses import dataclass
7
9
8
10
from pytest_embedded .log import DuplicateStdoutPopen
9
11
from qemu .qmp import QMPClient
14
16
from .app import QemuApp
15
17
16
18
19
+ @dataclass
20
+ class QemuTarget :
21
+ strap_mode : str
22
+ default_efuse : bytes
23
+
24
+
25
+ QEMU_TARGETS : dict [str , QemuTarget ] = {
26
+ 'esp32' : QemuTarget (
27
+ strap_mode = '0x0F' ,
28
+ default_efuse = binascii .unhexlify (
29
+ '00000000000000000000000000800000000000000000100000000000000000000000000000000000'
30
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
31
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
32
+ '00000000'
33
+ ),
34
+ ),
35
+ 'esp32c3' : QemuTarget (
36
+ strap_mode = '0x02' ,
37
+ default_efuse = binascii .unhexlify (
38
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000c00'
39
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
40
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
41
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
42
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
43
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
44
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
45
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
46
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
47
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
48
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
49
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
50
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
51
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
52
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
53
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
54
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
55
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
56
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
57
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
58
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
59
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
60
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
61
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
62
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
63
+ '000000000000000000000000000000000000000000000000'
64
+ ),
65
+ ),
66
+ 'esp32s3' : QemuTarget (
67
+ strap_mode = '0x07' ,
68
+ default_efuse = binascii .unhexlify (
69
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000c00'
70
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
71
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
72
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
73
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
74
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
75
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
76
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
77
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
78
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
79
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
80
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
81
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
82
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
83
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
84
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
85
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
86
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
87
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
88
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
89
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
90
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
91
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
92
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
93
+ '00000000000000000000000000000000000000000000000000000000000000000000000000000000'
94
+ '000000000000000000000000000000000000000000000000'
95
+ ),
96
+ ),
97
+ }
98
+
99
+
17
100
class Qemu (DuplicateStdoutPopen ):
18
101
"""
19
102
QEMU class
@@ -26,9 +109,8 @@ class Qemu(DuplicateStdoutPopen):
26
109
QEMU_DEFAULT_ARGS = '-nographic -machine esp32'
27
110
QEMU_DEFAULT_FMT = '-nographic -machine {}'
28
111
29
- QEMU_STRAP_MODE_FMT = '-global driver=esp32 .gpio,property=strap_mode,value={}'
112
+ QEMU_STRAP_MODE_FMT = 'driver={} .gpio,property=strap_mode,value={}'
30
113
QEMU_SERIAL_TCP_FMT = '-serial tcp::{},server,nowait'
31
-
32
114
QEMU_DEFAULT_QMP_FMT = '-qmp tcp:127.0.0.1:{},server,wait=off'
33
115
34
116
def __init__ (
@@ -37,6 +119,7 @@ def __init__(
37
119
qemu_prog_path : str | None = None ,
38
120
qemu_cli_args : str | None = None ,
39
121
qemu_extra_args : str | None = None ,
122
+ qemu_efuse_path : str | None = None ,
40
123
app : t .Optional ['QemuApp' ] = None ,
41
124
** kwargs ,
42
125
):
@@ -53,13 +136,28 @@ def __init__(
53
136
if not os .path .exists (image_path ):
54
137
raise ValueError (f"QEMU image path doesn't exist: { image_path } " )
55
138
56
- qemu_prog_path = qemu_prog_path or self .qemu_prog_name
139
+ self .qemu_prog_path = qemu_prog_path or self .qemu_prog_name
140
+ self .image_path = image_path
141
+ self .efuse_path = qemu_efuse_path
57
142
58
143
if qemu_cli_args :
59
144
qemu_cli_args = qemu_cli_args .strip ('"' ).strip ("'" )
60
145
qemu_cli_args = shlex .split (qemu_cli_args or self .qemu_default_args )
61
146
qemu_extra_args = shlex .split (qemu_extra_args or '' )
62
147
148
+ if self .efuse_path :
149
+ logging .debug ('The eFuse file will be saved to: %s' , self .efuse_path )
150
+ with open (self .efuse_path , 'wb' ) as f :
151
+ f .write (QEMU_TARGETS [self .app .target ].default_efuse )
152
+ qemu_extra_args += [
153
+ '-global' ,
154
+ self .QEMU_STRAP_MODE_FMT .format (self .app .target , QEMU_TARGETS [self .app .target ].strap_mode ),
155
+ '-drive' ,
156
+ f'file={ self .efuse_path } ,if=none,format=raw,id=efuse' ,
157
+ '-global' ,
158
+ f'driver=nvram.{ self .app .target } .efuse,property=drive,value=efuse' ,
159
+ ]
160
+
63
161
self .qmp_addr = None
64
162
self .qmp_port = None
65
163
@@ -83,10 +181,59 @@ def __init__(
83
181
qemu_cli_args += shlex .split (self .QEMU_DEFAULT_QMP_FMT .format (self .qmp_port ))
84
182
85
183
super ().__init__ (
86
- cmd = [qemu_prog_path , * qemu_cli_args , * qemu_extra_args , '-drive' , f'file={ image_path } ,if=mtd,format=raw' ],
184
+ cmd = [
185
+ self .qemu_prog_path ,
186
+ * qemu_cli_args ,
187
+ * qemu_extra_args ,
188
+ '-drive' ,
189
+ f'file={ image_path } ,if=mtd,format=raw' ,
190
+ ],
87
191
** kwargs ,
88
192
)
89
193
194
+ def execute_efuse_command (self , command : str ):
195
+ import espefuse
196
+ import pexpect
197
+
198
+ with socket .socket (socket .AF_INET , socket .SOCK_STREAM ) as s :
199
+ s .bind (('127.0.0.1' , 0 ))
200
+ _ , available_port = s .getsockname ()
201
+
202
+ run_qemu_command = [
203
+ '-nographic' ,
204
+ '-machine' ,
205
+ self .app .target ,
206
+ '-drive' ,
207
+ f'file={ self .image_path } ,if=mtd,format=raw' ,
208
+ '-global' ,
209
+ self .QEMU_STRAP_MODE_FMT .format (self .app .target , QEMU_TARGETS [self .app .target ].strap_mode ),
210
+ '-drive' ,
211
+ f'file={ self .efuse_path } ,if=none,format=raw,id=efuse' ,
212
+ '-global' ,
213
+ f'driver=nvram.{ self .app .target } .efuse,property=drive,value=efuse' ,
214
+ '-serial' ,
215
+ f'tcp::{ available_port } ,server,nowait' ,
216
+ ]
217
+ try :
218
+ child = pexpect .spawn (self .qemu_prog_path , run_qemu_command )
219
+ res = shlex .split (command )
220
+ child .expect ('qemu' )
221
+
222
+ res = [r for r in res if r != '--do-not-confirm' ]
223
+ espefuse .main (
224
+ [
225
+ '--port' ,
226
+ f'socket://localhost:{ available_port } ' ,
227
+ '--before' ,
228
+ 'no-reset' ,
229
+ '--do-not-confirm' ,
230
+ * res ,
231
+ ]
232
+ )
233
+ self ._hard_reset ()
234
+ finally :
235
+ child .terminate ()
236
+
90
237
@property
91
238
def qemu_prog_name (self ):
92
239
if self .app :
0 commit comments