1010import os
1111import os .path
1212import platform
13+ import re
1314import shutil
1415import signal
1516import socket
2930logger = logging .getLogger ("pebble_tool.sdk.emulator" )
3031black_hole = open (os .devnull , 'w' )
3132
33+ _qemu_version_cache = None
34+
35+ def get_qemu_version ():
36+ """Detect the QEMU major version. Returns the major version as an int, or None if detection fails."""
37+ global _qemu_version_cache
38+ if _qemu_version_cache is not None :
39+ return _qemu_version_cache
40+ qemu_bin = os .environ .get ('PEBBLE_QEMU_PATH' , 'qemu-pebble' )
41+ try :
42+ result = subprocess .check_output ([qemu_bin , '--version' ], stderr = subprocess .STDOUT , text = True )
43+ match = re .search (r'version (\d+)\.' , result )
44+ if match :
45+ _qemu_version_cache = int (match .group (1 ))
46+ return _qemu_version_cache
47+ except (subprocess .CalledProcessError , OSError ):
48+ pass
49+ return None
50+
3251def _to_text (data ):
3352 if data is None :
3453 return ''
@@ -266,15 +285,32 @@ def _spawn_qemu(self):
266285 if not os .path .exists (path ):
267286 raise MissingEmulatorError ("Can't launch emulator: missing required file at {}" .format (path ))
268287
288+ qemu_major = get_qemu_version ()
289+ logger .info ("Detected QEMU major version: %s" , qemu_major )
290+
291+ new_qemu = qemu_major is not None and qemu_major >= 3
292+
293+ # QEMU 3+ uses server=on,wait=off; older versions use server,nowait
294+ if new_qemu :
295+ tcp_opts = "server=on,wait=off"
296+ else :
297+ tcp_opts = "server,nowait"
298+
299+ # QEMU 10+ loads firmware via -kernel; older versions use -pflash
300+ if new_qemu :
301+ fw_args = ["-kernel" , qemu_micro_flash ]
302+ else :
303+ fw_args = ["-pflash" , qemu_micro_flash ]
304+
269305 command = [
270306 qemu_bin ,
271307 "-rtc" , "base=localtime" ,
272308 "-serial" , "null" ,
273- "-serial" , "tcp::{},server,nowait " .format (self .qemu_port ),
274- "-serial" , "tcp::{},server,nowait " .format (self .qemu_serial_port ),
275- "-pflash" , qemu_micro_flash ,
276- "-gdb" , "tcp::{},server,nowait " .format (self .qemu_gdb_port ),
277- "-monitor" , "tcp::{},server,nowait " .format (self .qemu_monitor_port ),
309+ "-serial" , "tcp::{},{} " .format (self .qemu_port , tcp_opts ),
310+ "-serial" , "tcp::{},{} " .format (self .qemu_serial_port , tcp_opts ),
311+ ] + fw_args + [
312+ "-gdb" , "tcp::{},{} " .format (self .qemu_gdb_port , tcp_opts ),
313+ "-monitor" , "tcp::{},{} " .format (self .qemu_monitor_port , tcp_opts ),
278314 ]
279315
280316 if self .vnc_enabled :
@@ -291,12 +327,17 @@ def _spawn_qemu(self):
291327 if parse_version (version_base ) < parse_version ('4.9' ):
292328 emery_machine = 'pebble-robert-bb'
293329
330+ # QEMU 10+ uses named block device for SPI; older versions use -pflash
331+ if new_qemu :
332+ spi_pflash = ['-drive' , 'if=none,id=spi-flash,file={},format=raw' .format (qemu_spi_flash )]
333+ else :
334+ spi_pflash = ['-pflash' , qemu_spi_flash ]
335+
294336 platform_args = {
295337 'gabbro' : [
296338 '-machine' , 'pebble-spalding-gabbro-bb' ,
297339 '-cpu' , 'cortex-m4' ,
298- '-pflash' , qemu_spi_flash ,
299- ],
340+ ] + spi_pflash ,
300341 'flint' : [
301342 '-machine' , 'pebble-silk-bb' ,
302343 '-cpu' , 'cortex-m4' ,
@@ -305,8 +346,7 @@ def _spawn_qemu(self):
305346 'emery' : [
306347 '-machine' , emery_machine ,
307348 '-cpu' , 'cortex-m4' ,
308- '-pflash' , qemu_spi_flash ,
309- ],
349+ ] + spi_pflash ,
310350 'diorite' : [
311351 '-machine' , 'pebble-silk-bb' ,
312352 '-cpu' , 'cortex-m4' ,
@@ -315,13 +355,11 @@ def _spawn_qemu(self):
315355 'chalk' : [
316356 '-machine' , 'pebble-s4-bb' ,
317357 '-cpu' , 'cortex-m4' ,
318- '-pflash' , qemu_spi_flash ,
319- ],
358+ ] + spi_pflash ,
320359 'basalt' : [
321360 '-machine' , 'pebble-snowy-bb' ,
322361 '-cpu' , 'cortex-m4' ,
323- '-pflash' , qemu_spi_flash ,
324- ],
362+ ] + spi_pflash ,
325363 'aplite' : [
326364 '-machine' , 'pebble-bb2' ,
327365 '-cpu' , 'cortex-m3' ,
0 commit comments