3333#include "qemu/timer.h"
3434#include "qemu/typedefs.h"
3535#include "qapi/error.h"
36+ #include "chardev/char-fe.h"
3637#include "hw/hw.h"
3738#include "hw/opentitan/ot_alert.h"
3839#include "hw/opentitan/ot_gpio.h"
4445#include "hw/sysbus.h"
4546#include "trace.h"
4647
48+ /*
49+ * Unfortunately, there is no QEMU API to properly disable serial control lines
50+ */
51+ #ifndef _WIN32
52+ #include <termios.h>
53+ #include "chardev/char-fd.h"
54+ #include "io/channel-file.h"
55+ #endif
56+
57+
4758#define PARAM_NUM_ALERTS 1u
4859
4960/* clang-format off */
@@ -113,7 +124,12 @@ struct OtGpioState {
113124 uint32_t data_oe ;
114125 uint32_t data_in ;
115126
127+ char ibuf [32u ]; /* backed input buffer */
128+ unsigned ipos ;
129+
116130 uint32_t reset_in ; /* initial input levels */
131+ CharBackend chr ; /* communication device */
132+ guint watch_tag ; /* tracker for comm device change */
117133};
118134
119135static void ot_gpio_update_irqs (OtGpioState * s )
@@ -161,6 +177,30 @@ static void ot_gpio_update_data_in(OtGpioState *s)
161177 ot_gpio_update_irqs (s );
162178}
163179
180+ static void ot_gpio_update_backend (OtGpioState * s , bool oe )
181+ {
182+ if (!qemu_chr_fe_backend_connected (& s -> chr )) {
183+ return ;
184+ }
185+
186+ char buf [32u ];
187+ size_t len ;
188+
189+ /*
190+ * use the infamous MS DOS CR LF syntax because people can't help using
191+ * Windows-style terminal
192+ */
193+ if (oe ) {
194+ len = snprintf (& buf [0 ], sizeof (buf ), "D:%08x\r\n" , s -> data_oe );
195+ } else {
196+ len = 0 ;
197+ }
198+
199+ len += snprintf (& buf [len ], sizeof (buf ), "O:%08x\r\n" , s -> data_out );
200+
201+ qemu_chr_fe_write (& s -> chr , (const uint8_t * )buf , len );
202+ }
203+
164204static uint64_t ot_gpio_read (void * opaque , hwaddr addr , unsigned size )
165205{
166206 OtGpioState * s = opaque ;
@@ -246,39 +286,45 @@ static void ot_gpio_write(void *opaque, hwaddr addr, uint64_t val64,
246286 case R_DIRECT_OUT :
247287 s -> regs [reg ] = val32 ;
248288 s -> data_out = val32 ;
289+ ot_gpio_update_backend (s , false);
249290 ot_gpio_update_data_in (s );
250291 break ;
251292 case R_DIRECT_OE :
252293 s -> regs [reg ] = val32 ;
253294 s -> data_oe = val32 ;
295+ ot_gpio_update_backend (s , true);
254296 ot_gpio_update_data_in (s );
255297 break ;
256298 case R_MASKED_OUT_LOWER :
257299 s -> regs [reg ] = val32 ;
258300 mask = val32 >> MASKED_MASK_SHIFT ;
259301 s -> data_out &= ~mask ;
260302 s -> data_out |= val32 & mask ;
303+ ot_gpio_update_backend (s , false);
261304 ot_gpio_update_data_in (s );
262305 break ;
263306 case R_MASKED_OUT_UPPER :
264307 s -> regs [reg ] = val32 ;
265308 mask = val32 & MASKED_MASK_MASK ;
266309 s -> data_out &= ~mask ;
267310 s -> data_out |= (val32 << MASKED_MASK_SHIFT ) & mask ;
311+ ot_gpio_update_backend (s , false);
268312 ot_gpio_update_data_in (s );
269313 break ;
270314 case R_MASKED_OE_LOWER :
271315 s -> regs [reg ] = val32 ;
272316 mask = val32 >> MASKED_MASK_SHIFT ;
273317 s -> data_oe &= ~mask ;
274318 s -> data_oe |= val32 & mask ;
319+ ot_gpio_update_backend (s , true);
275320 ot_gpio_update_data_in (s );
276321 break ;
277322 case R_MASKED_OE_UPPER :
278323 s -> regs [reg ] = val32 ;
279324 mask = val32 & MASKED_MASK_MASK ;
280325 s -> data_oe &= ~mask ;
281326 s -> data_oe |= (val32 << MASKED_MASK_SHIFT ) & mask ;
327+ ot_gpio_update_backend (s , true);
282328 ot_gpio_update_data_in (s );
283329 break ;
284330 case R_INTR_CTRL_EN_RISING :
@@ -306,6 +352,124 @@ static void ot_gpio_write(void *opaque, hwaddr addr, uint64_t val64,
306352 }
307353};
308354
355+ static int ot_gpio_chr_can_receive (void * opaque )
356+ {
357+ OtGpioState * s = opaque ;
358+
359+ return (int )sizeof (s -> ibuf ) - (int )s -> ipos ;
360+ }
361+
362+ static void ot_gpio_chr_receive (void * opaque , const uint8_t * buf , int size )
363+ {
364+ OtGpioState * s = opaque ;
365+
366+ if (s -> ipos + (unsigned )size > sizeof (s -> ibuf )) {
367+ qemu_log ("%s: Incoherent chardev receive\n" , __func__ );
368+ return ;
369+ }
370+
371+ memcpy (& s -> ibuf [s -> ipos ], buf , (size_t )size );
372+ s -> ipos += (unsigned )size ;
373+
374+ for (;;) {
375+ const char * eol = memchr (s -> ibuf , (int )'\n' , s -> ipos );
376+ if (!eol ) {
377+ if (s -> ipos > 10u ) {
378+ /* discard any garbage */
379+ memset (s -> ibuf , 0 , sizeof (s -> ibuf ));
380+ s -> ipos = 0 ;
381+ }
382+ return ;
383+ }
384+ unsigned eolpos = eol - s -> ibuf ;
385+ if (eolpos < 10u ) {
386+ memmove (s -> ibuf , eol + 1u , eolpos + 1u );
387+ s -> ipos = 0 ;
388+ continue ;
389+ }
390+ uint32_t data_in = 0 ;
391+ char cmd = '\0' ;
392+ int ret = sscanf (s -> ibuf , "%c:%08x" , & cmd , & data_in );
393+ memmove (s -> ibuf , eol + 1u , eolpos + 1u );
394+ s -> ipos = 0 ;
395+
396+ if (ret == 2 ) {
397+ if (cmd == 'I' ) {
398+ s -> data_in = data_in ;
399+ ot_gpio_update_data_in (s );
400+ } else if (cmd == 'R' ) {
401+ ot_gpio_update_backend (s , true);
402+ }
403+ }
404+ }
405+ }
406+
407+ static void ot_gpio_chr_ignore_status_lines (OtGpioState * s )
408+ {
409+ /* it might be useful to move this to char-serial.c */
410+ #ifndef _WIN32
411+ FDChardev * cd = FD_CHARDEV (s -> chr .chr );
412+ QIOChannelFile * fioc = QIO_CHANNEL_FILE (cd -> ioc_in );
413+
414+ struct termios tty = { 0 };
415+ tcgetattr (fioc -> fd , & tty );
416+ tty .c_cflag |= CLOCAL ; /* ignore modem status lines */
417+ tcsetattr (fioc -> fd , TCSANOW , & tty );
418+ #endif
419+ }
420+
421+ static void ot_gpio_chr_event_hander (void * opaque , QEMUChrEvent event )
422+ {
423+ OtGpioState * s = opaque ;
424+
425+ if (event == CHR_EVENT_OPENED ) {
426+ if (object_dynamic_cast (OBJECT (s -> chr .chr ), TYPE_CHARDEV_SERIAL )) {
427+ ot_gpio_chr_ignore_status_lines (s );
428+ }
429+
430+ ot_gpio_update_backend (s , true);
431+
432+ if (!qemu_chr_fe_backend_connected (& s -> chr )) {
433+ return ;
434+ }
435+
436+ /* query backend for current input status */
437+ char buf [16u ];
438+ int len = snprintf (buf , sizeof (buf ), "Q:%08x\r\n" , s -> data_oe );
439+ qemu_chr_fe_write (& s -> chr , (const uint8_t * )buf , len );
440+ }
441+ }
442+
443+ static gboolean ot_gpio_chr_watch_cb (void * do_not_use , GIOCondition cond ,
444+ void * opaque )
445+ {
446+ OtGpioState * s = opaque ;
447+
448+ s -> watch_tag = 0 ;
449+
450+ return FALSE;
451+ }
452+
453+ static int ot_gpio_chr_be_change (void * opaque )
454+ {
455+ OtGpioState * s = opaque ;
456+
457+ qemu_chr_fe_set_handlers (& s -> chr , & ot_gpio_chr_can_receive ,
458+ & ot_gpio_chr_receive , & ot_gpio_chr_event_hander ,
459+ & ot_gpio_chr_be_change , s , NULL , true);
460+
461+ memset (s -> ibuf , 0 , sizeof (s -> ibuf ));
462+ s -> ipos = 0 ;
463+
464+ if (s -> watch_tag > 0 ) {
465+ g_source_remove (s -> watch_tag );
466+ s -> watch_tag = qemu_chr_fe_add_watch (& s -> chr , G_IO_OUT | G_IO_HUP ,
467+ & ot_gpio_chr_watch_cb , s );
468+ }
469+
470+ return 0 ;
471+ }
472+
309473static const MemoryRegionOps ot_gpio_regs_ops = {
310474 .read = & ot_gpio_read ,
311475 .write = & ot_gpio_write ,
@@ -316,6 +480,7 @@ static const MemoryRegionOps ot_gpio_regs_ops = {
316480
317481static Property ot_gpio_properties [] = {
318482 DEFINE_PROP_UINT32 ("in" , OtGpioState , reset_in , 0u ),
483+ DEFINE_PROP_CHR ("chardev" , OtGpioState , chr ),
319484 DEFINE_PROP_END_OF_LIST (),
320485};
321486
@@ -331,6 +496,22 @@ static void ot_gpio_reset(DeviceState *dev)
331496
332497 ot_gpio_update_irqs (s );
333498 ibex_irq_set (& s -> alert , 0 );
499+
500+ ot_gpio_update_backend (s , true);
501+
502+ /*
503+ * do not reset the input backed buffer as external GPIO changes is fully
504+ * async with OT reset. However, it should be reset when the backend changes
505+ */
506+ }
507+
508+ static void ot_gpio_realize (DeviceState * dev , Error * * errp )
509+ {
510+ OtGpioState * s = OT_GPIO (dev );
511+
512+ qemu_chr_fe_set_handlers (& s -> chr , & ot_gpio_chr_can_receive ,
513+ & ot_gpio_chr_receive , & ot_gpio_chr_event_hander ,
514+ & ot_gpio_chr_be_change , s , NULL , true);
334515}
335516
336517static void ot_gpio_init (Object * obj )
@@ -352,6 +533,7 @@ static void ot_gpio_class_init(ObjectClass *klass, void *data)
352533 DeviceClass * dc = DEVICE_CLASS (klass );
353534
354535 dc -> reset = & ot_gpio_reset ;
536+ dc -> realize = & ot_gpio_realize ;
355537 device_class_set_props (dc , ot_gpio_properties );
356538 set_bit (DEVICE_CATEGORY_MISC , dc -> categories );
357539}
0 commit comments