Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

single-byte console writes never complete #1736

Closed
bcantrill opened this issue Apr 6, 2020 · 9 comments · Fixed by #1817
Closed

single-byte console writes never complete #1736

bcantrill opened this issue Apr 6, 2020 · 9 comments · Fixed by #1817
Labels

Comments

@bcantrill
Copy link
Contributor

bcantrill commented Apr 6, 2020

Currently, the timer example from libtock-rs hangs, after emitting a single [:

Version:    opentitan-snapshot-20191101-1-887-ge849d530
Build Date: 2020-04-03, 11:01:10
I00000 boot_rom.c:44] Boot ROM initialisation has completed, jump into flash!
OpenTitan initialisation complete. Entering main loop
[

I looked at a trace under Tockilator (and because it's instructive, I have also pushed everything needed to reproduce this to the example/libtock-rs/timer subdirectory).

For starters, here is the output showing just system calls:

% tockilator ./trace_core_00000000.log
169204   => SYSCALL MEMOP operand=0 (brk) arg0=0x10003004
187307   <= SYSCALL MEMOP operand=0 (brk) arg0=0x10003004
187310   => SYSCALL MEMOP operand=10 (update-stack-start) arg0=0x10003400
202583   <= SYSCALL MEMOP operand=10 (update-stack-start) arg0=0x10003400
202586   => SYSCALL MEMOP operand=11 (update-heap-start) arg0=0x10003004
217281   <= SYSCALL MEMOP operand=11 (update-heap-start) arg0=0x10003004
217358   => SYSCALL MEMOP operand=0 (brk) arg0=0x10003404
234333   <= SYSCALL MEMOP operand=0 (brk) arg0=0x10003404
235019   => SYSCALL COMMAND driver=0 (timer) subdriver=0 arg0=0x0 arg1=0x0
250366   <= SYSCALL COMMAND driver=0 (timer) subdriver=0 arg0=0x0 arg1=0x0
250373   => SYSCALL COMMAND driver=0 (timer) subdriver=1 arg0=0x0 arg1=0x0
265330   <= SYSCALL COMMAND driver=0 (timer) subdriver=1 arg0=0x0 arg1=0x0
265339   => SYSCALL SUBSCRIBE driver=0 (timer) subdriver=0 cb=0x20032006 data=0x1000331c
281047   <= SYSCALL SUBSCRIBE driver=0 (timer) subdriver=0 cb=0x20032006 data=0x1000331c
281059   => SYSCALL COMMAND driver=0 (timer) subdriver=2 arg0=0x0 arg1=0x0
296016   <= SYSCALL COMMAND driver=0 (timer) subdriver=2 arg0=0x0 arg1=0x0
297243   => SYSCALL ALLOW driver=1 (console) subdriver=1 addr=0x100032d4 len=1
314794   <= SYSCALL ALLOW driver=1 (console) subdriver=1 addr=0x100032d4 len=1
314805   => SYSCALL SUBSCRIBE driver=1 (console) subdriver=1 cb=0x20031294 data=0x10003168
330553   <= SYSCALL SUBSCRIBE driver=1 (console) subdriver=1 cb=0x20031294 data=0x10003168
330559   => SYSCALL COMMAND driver=1 (console) subdriver=1 arg0=0x1 arg1=0x0
346462   <= SYSCALL COMMAND driver=1 (console) subdriver=1 arg0=0x1 arg1=0x0
346479   => SYSCALL YIELD

So we're doing a one-byte write to the console -- and if the subscribe callback is called, it's certainly not making any other system calls. For starters, it's helpful to confirm this by specifying the Tock application timer.elf binary to Tockilator:

% tockilator -e ./timer.elf ./trace_core_00000000.log
...
297041      | write_fmt<libtock::console::Console>
297068      -> write
297111         | iter<core::fmt::ArgumentV1>
297111           | add<core::fmt::ArgumentV1>
297111             | offset<core::fmt::ArgumentV1>
297134         | zip<core::slice::Iter<core::fmt::rt::v1::Argument>,core::slice::Iter<&str>>
297134           | new<core::slice::Iter<core::fmt::rt::v1::Argument>,core::slice::Iter<&str>>
297134             | new<core::slice::Iter<core::fmt::rt::v1::Argument>,core::slice::Iter<&str>>
297134               | min<usize>
297134                 | min<usize>
297134                   | min_by<usize,fn(&usize, &usize) -> core::cmp::Ordering>
297139         | next<core::slice::Iter<core::fmt::rt::v1::Argument>,core::slice::Iter<&str>>
297139           | next<core::slice::Iter<core::fmt::rt::v1::Argument>,core::slice::Iter<&str>>
297148        -> write_str<libtock::console::Console>
297208           | copy_from_slice<u8>
297208             | copy_nonoverlapping<u8>
297214            -> memcpy
297236            <- memcpy
297237           | copy_from_slice<u8>
297237             | copy_nonoverlapping<u8>
297238           | allow
297238             | allow
297243               => SYSCALL ALLOW driver=1 (console) subdriver=1 addr=0x100032d4 len=1
314794               <= SYSCALL ALLOW driver=1 (console) subdriver=1 addr=0x100032d4 len=1
314794         | allow
314794           | allow
314800         | subscribe<libtock_core::callback::Identity0Consumer,closure-0>
314800           | subscribe_fn
314800             | subscribe
314805               => SYSCALL SUBSCRIBE driver=1 (console) subdriver=1 cb=0x20031294 data=0x10003168
330553               <= SYSCALL SUBSCRIBE driver=1 (console) subdriver=1 cb=0x20031294 data=0x10003168
330553         | subscribe<libtock_core::callback::Identity0Consumer,closure-0>
330553           | subscribe_fn
330553             | subscribe
330554         | command
330554           | command
330559             => SYSCALL COMMAND driver=1 (console) subdriver=1 arg0=0x1 arg1=0x0
346462             <= SYSCALL COMMAND driver=1 (console) subdriver=1 arg0=0x1 arg1=0x0
346462         | command
346462           | command
346469         | poll_with_tls_context<libtock::futures::WaitForValue<closure-0>>
346469           | poll<libtock::futures::WaitForValue<closure-0>>
346469             | poll<(),closure-0>
346478         | yieldk
346478           | yieldk
346479             => SYSCALL YIELD

This shows us that we're not getting back to user-level. To show what's going on, let's look at Tock itself from cycle 353912 on:

% tockilator -e ./opentitan.elf -vp ./trace_core_00000000.log -c 353912-
353912 -> call (GOFF 0x13437)
353912    ( _handle=0x0 (GOFF 0x1345b)
353912    ( self=0x10002108 (GOFF 0x1344d)
353940    | is_none<&capsules::virtual_uart::UartDevice> (GOFF 0x1432b)
353940      ( self=0x10002118 (GOFF 0x1433b)
353940      | get<core::option::Option<&capsules::virtual_uart::UartDevice>> (GOFF 0x14344)
353940        ( self=0x10002118 (GOFF 0x14354)
353946    | find<kernel::common::list::ListIterator<capsules::virtual_uart::UartDevice>,closure-0> (GOFF 0x1435f)
353946      | try_fold<kernel::common::list::ListIterator<capsules::virtual_uart::UartDevice>,(),closure-0,core::iter::LoopState<(), &capsules::virtual_uart::UartDevice>> (GOFF 0x14374)
353946        | next<capsules::virtual_uart::UartDevice> (GOFF 0x1438a)
353947          | next (GOFF 0x143b2)
353947            ( self=0x10002064 (GOFF 0x143c2)
353949        | {{closure}}<&capsules::virtual_uart::UartDevice,closure-0> (GOFF 0x143e0)
353949          ( x=0x10002064 (GOFF 0x143f1)
353949          | {{closure}} (GOFF 0x143fa)
353949            | is_some<capsules::virtual_uart::Operation> (GOFF 0x14410)
353949              | get<core::option::Option<capsules::virtual_uart::Operation>> (GOFF 0x14420)
353955    | find<kernel::common::list::ListIterator<capsules::virtual_uart::UartDevice>,closure-0> (GOFF 0x1435f)
353955      | try_fold<kernel::common::list::ListIterator<capsules::virtual_uart::UartDevice>,(),closure-0,core::iter::LoopState<(), &capsules::virtual_uart::UartDevice>> (GOFF 0x14374)
353955        | next<capsules::virtual_uart::UartDevice> (GOFF 0x1438a)
353956          | next (GOFF 0x143b2)
353956            ( self=0x100025a8 (GOFF 0x143c2)
353958        | {{closure}}<&capsules::virtual_uart::UartDevice,closure-0> (GOFF 0x143e0)
353958          ( x=0x100025a8 (GOFF 0x143f1)
353958          | {{closure}} (GOFF 0x143fa)
353958            | is_some<capsules::virtual_uart::Operation> (GOFF 0x14410)
353958              | get<core::option::Option<capsules::virtual_uart::Operation>> (GOFF 0x14420)
353964    | find<kernel::common::list::ListIterator<capsules::virtual_uart::UartDevice>,closure-0> (GOFF 0x1435f)
353964      | try_fold<kernel::common::list::ListIterator<capsules::virtual_uart::UartDevice>,(),closure-0,core::iter::LoopState<(), &capsules::virtual_uart::UartDevice>> (GOFF 0x14374)
353964        | next<capsules::virtual_uart::UartDevice> (GOFF 0x1438a)
353965          | next (GOFF 0x143b2)
353965            ( self=0x10002134 (GOFF 0x143c2)
353967        | {{closure}}<&capsules::virtual_uart::UartDevice,closure-0> (GOFF 0x143e0)
353967          ( x=0x10002134 (GOFF 0x143f1)
353967          | {{closure}} (GOFF 0x143fa)
353967            | is_some<capsules::virtual_uart::Operation> (GOFF 0x14410)
353967              | get<core::option::Option<capsules::virtual_uart::Operation>> (GOFF 0x14420)
353970    | map<&capsules::virtual_uart::UartDevice,(),closure-1> (GOFF 0x1444a)
353970      ( self=0x10002134 (GOFF 0x14456)
353970      | {{closure}} (GOFF 0x14472)
353970        ( node=0x10002134 (GOFF 0x1447f)
353970        | take<[u8]> (GOFF 0x1448d)
353970          ( self=0x10002138 (GOFF 0x1449d)
353970          | replace<core::option::Option<&mut [u8]>> (GOFF 0x144a6)
353970            ( val=0x0 (GOFF 0x144bf)
353970            ( self=0x10002138 (GOFF 0x144b6)
353970            | replace<core::option::Option<&mut [u8]>> (GOFF 0x144c8)
353970              ( dest=0x10002138 (GOFF 0x144d9)
353970              | swap<core::option::Option<&mut [u8]>> (GOFF 0x144e7)
353970                ( x=0x10002138 (GOFF 0x144f8)
353970                | swap_nonoverlapping_one<core::option::Option<&mut [u8]>> (GOFF 0x14506)
353970                  ( x=0x10002138 (GOFF 0x14517)
353970                  | read<core::option::Option<&mut [u8]>> (GOFF 0x14525)
353970                    ( src=0x10002138 (GOFF 0x14536)
353970                    | copy_nonoverlapping<core::option::Option<&mut [u8]>> (GOFF 0x1454d)
353970                      ( dst=0x10002138 (GOFF 0x145a0)
353970                      ( count=0x1 (GOFF 0x1456c)
353970                      ( src=0x10002138 (GOFF 0x1455e)
353972                  | copy_nonoverlapping<core::option::Option<&mut [u8]>> (GOFF 0x1458a)
353972                    ( count=0x1 (GOFF 0x145a9)
353972                    ( dst=0x10002138 (GOFF 0x145a0)
353972                    ( count=0x1 (GOFF 0x1456c)
353972                    ( src=0x10002138 (GOFF 0x1455e)
353973        | map<&mut [u8],(),closure-0> (GOFF 0x145b9)
353973          ( self=0x10001fe4 (GOFF 0x145c5)
353975          | {{closure}} (GOFF 0x145e1)
353975            ( buf=0x10001fe4 (GOFF 0x145ee)
353975            | map<capsules::virtual_uart::Operation,closure-0,()> (GOFF 0x145fc)
353975              | get<core::option::Option<capsules::virtual_uart::Operation>> (GOFF 0x14611)
353978              | map<capsules::virtual_uart::Operation,(),closure-0> (GOFF 0x1462b)
353978                ( self=0x1 (GOFF 0x14637)
353978                | {{closure}}<capsules::virtual_uart::Operation,closure-0,()> (GOFF 0x1464e)
353978                  | {{closure}} (GOFF 0x14660)
353983        | map<&mut [u8],(),closure-0> (GOFF 0x145b9)
353983          ( self=0x10001fe4 (GOFF 0x145c5), 0x40 (GOFF 0x145c5)
353983          | {{closure}} (GOFF 0x145e1)
353983            ( buf=0x10001fe4 (GOFF 0x145ee), 0x40 (GOFF 0x145ee)
353983            | map<capsules::virtual_uart::Operation,closure-0,()> (GOFF 0x145fc)
353983              | map<capsules::virtual_uart::Operation,(),closure-0> (GOFF 0x1462b)
353983                ( self=0x1 (GOFF 0x14637)
353983                | {{closure}}<capsules::virtual_uart::Operation,closure-0,()> (GOFF 0x1464e)
353983                  | {{closure}} (GOFF 0x14660)
353991           -> transmit_buffer (GOFF 0x418ab)
353991              ( tx_len=0x1 (GOFF 0x418e5)
353991              ( tx_data=0x10001fe4 (GOFF 0x418d5), 0x40 (GOFF 0x418d5)
353991              ( self=0x100015c8 (GOFF 0x418c5)
354002              | is_some<[u8]> (GOFF 0x418f5)
354002                ( self=0x100015e0 (GOFF 0x41906)
354002                | is_none<[u8]> (GOFF 0x4190f)
354002                  ( self=0x100015e0 (GOFF 0x4191f)
354002                  | take<[u8]> (GOFF 0x41928)
354002                    ( self=0x100015e0 (GOFF 0x41938)
354002                    | replace<core::option::Option<&mut [u8]>> (GOFF 0x41941)
354002                      ( self=0x100015e0 (GOFF 0x41b22)
354002                      ( val=0x0 (GOFF 0x4195a)
354002                      ( self=0x100015e0 (GOFF 0x41951)
354002                      | replace<core::option::Option<&mut [u8]>> (GOFF 0x41963)
354002                        ( dest=0x100015e0 (GOFF 0x41b45)
354002                        ( dest=0x100015e0 (GOFF 0x41974)
354002                        | swap<core::option::Option<&mut [u8]>> (GOFF 0x41982)
354002                          ( x=0x100015e0 (GOFF 0x41b64)
354002                          ( x=0x100015e0 (GOFF 0x41993)
354002                          | swap_nonoverlapping_one<core::option::Option<&mut [u8]>> (GOFF 0x419a1)
354002                            ( x=0x100015e0 (GOFF 0x41b83)
354002                            ( x=0x100015e0 (GOFF 0x419b2)
354002                            | read<core::option::Option<&mut [u8]>> (GOFF 0x419c0)
354002                              ( src=0x100015e0 (GOFF 0x419d1)
354002                              | copy_nonoverlapping<core::option::Option<&mut [u8]>> (GOFF 0x419e8)
354002                                ( dst=0x100015e0 (GOFF 0x41ba7)
354002                                ( count=0x1 (GOFF 0x41a07)
354002                                ( src=0x100015e0 (GOFF 0x419f9)
354007              | set<usize> (GOFF 0x41a1a)
354007                ( val=0x1 (GOFF 0x41a34)
354007                ( self=0x100015e8 (GOFF 0x41a2b)
354007                | replace<usize> (GOFF 0x41a3d)
354007                  ( val=0x1 (GOFF 0x41a57)
354007                  ( self=0x100015e8 (GOFF 0x41a4e)
354007                  | replace<usize> (GOFF 0x41a60)
354007                    ( src=0x1 (GOFF 0x41a7a)
354007                    ( dest=0x100015e8 (GOFF 0x41a71)
354007                    | swap<usize> (GOFF 0x41a83)
354007                      ( x=0x100015e8 (GOFF 0x41a94)
354007                      | swap_nonoverlapping_one<usize> (GOFF 0x41a9d)
354007                        ( x=0x100015e8 (GOFF 0x41aae)
354007                        | copy_nonoverlapping<usize> (GOFF 0x41ac5)
354007                          ( count=0x1 (GOFF 0x41adf)
354007                          ( dst=0x100015e8 (GOFF 0x41ad6)
354009              | replace<[u8]> (GOFF 0x41aef)
354009                ( val=0x10001fe4 (GOFF 0x41b09), 0x40 (GOFF 0x41b09)
354009                ( self=0x100015e0 (GOFF 0x41b00)
354009                | replace<core::option::Option<&mut [u8]>> (GOFF 0x41b12)
354009                  ( val=0x10001fe4 (GOFF 0x41b2b), 0x40 (GOFF 0x41b2b)
354009                  ( self=0x100015e0 (GOFF 0x41b22)
354009                  | replace<core::option::Option<&mut [u8]>> (GOFF 0x41b34)
354009                    ( dest=0x100015e0 (GOFF 0x41b45)
354009                    | swap<core::option::Option<&mut [u8]>> (GOFF 0x41b53)
354009                      ( x=0x100015e0 (GOFF 0x41b64)
354009                      | swap_nonoverlapping_one<core::option::Option<&mut [u8]>> (GOFF 0x41b72)
354009                        ( x=0x100015e0 (GOFF 0x41b83)
354009                        | copy_nonoverlapping<core::option::Option<&mut [u8]>> (GOFF 0x41b91)
354009                          ( count=0x1 (GOFF 0x41bb0)
354009                          ( dst=0x100015e0 (GOFF 0x41ba7)
354013              | set<usize> (GOFF 0x41bbf)
354013                ( val=0x0 (GOFF 0x41bd9)
354013                ( self=0x100015ec (GOFF 0x41bd0)
354013                ( val=0x1 (GOFF 0x41a34)
354013                ( self=0x100015e8 (GOFF 0x41a2b)
354013                | replace<usize> (GOFF 0x41be2)
354013                  ( val=0x0 (GOFF 0x41bfc)
354013                  ( self=0x100015ec (GOFF 0x41bf3)
354013                  ( val=0x1 (GOFF 0x41a57)
354013                  ( self=0x100015e8 (GOFF 0x41a4e)
354013                  | replace<usize> (GOFF 0x41c05)
354013                    ( src=0x0 (GOFF 0x41c1f)
354013                    ( dest=0x100015ec (GOFF 0x41c16)
354013                    ( dest=0x100015e8 (GOFF 0x41a71)
354013                    | swap<usize> (GOFF 0x41c28)
354013                      ( x=0x100015ec (GOFF 0x41c39)
354013                      ( x=0x100015e8 (GOFF 0x41a94)
354013                      | swap_nonoverlapping_one<usize> (GOFF 0x41c42)
354013                        ( x=0x100015ec (GOFF 0x41c53)
354013                        ( x=0x100015e8 (GOFF 0x41aae)
354013                        | copy_nonoverlapping<usize> (GOFF 0x41c6a)
354013                          ( count=0x1 (GOFF 0x41c84)
354013                          ( dst=0x100015ec (GOFF 0x41c7b)
354013                          ( count=0x1 (GOFF 0x41adf)
354013                          ( dst=0x100015e8 (GOFF 0x41ad6)
354018             -> tx_progress (GOFF 0x406bd)
354018                ( self=0x100015c8 (GOFF 0x406d2)
354020                | get<usize> (GOFF 0x40705)
354020                  ( self=0x100015e8 (GOFF 0x4101d)
354020                  ( self=0x100015e8 (GOFF 0x40715)
354027                | get<usize> (GOFF 0x4122d)
354027                  ( self=0x100015ec (GOFF 0x4123d)
354027                  ( self=0x100015ec (GOFF 0x41003)
354027                  ( self=0x100015e8 (GOFF 0x4101d)
354027                  ( self=0x100015e8 (GOFF 0x40715)
354032                | map<[u8],closure-0,()> (GOFF 0x40733)
354032                  ( self=0x100015e0 (GOFF 0x4073f)
354032                  | take<[u8]> (GOFF 0x40748)
354032                    ( self=0x100015e0 (GOFF 0x40758)
354032                    | replace<core::option::Option<&mut [u8]>> (GOFF 0x40761)
354032                      ( self=0x100015e0 (GOFF 0x40b5d)
354032                      ( val=0x0 (GOFF 0x4077a)
354032                      ( self=0x100015e0 (GOFF 0x40771)
354032                      | replace<core::option::Option<&mut [u8]>> (GOFF 0x40783)
354032                        ( dest=0x100015e0 (GOFF 0x40b80)
354032                        ( dest=0x100015e0 (GOFF 0x40794)
354032                        | swap<core::option::Option<&mut [u8]>> (GOFF 0x407a2)
354032                          ( x=0x100015e0 (GOFF 0x40b9f)
354032                          ( x=0x100015e0 (GOFF 0x407b3)
354032                          | swap_nonoverlapping_one<core::option::Option<&mut [u8]>> (GOFF 0x407c1)
354032                            ( x=0x100015e0 (GOFF 0x40bbe)
354032                            ( x=0x100015e0 (GOFF 0x407d2)
354032                            | read<core::option::Option<&mut [u8]>> (GOFF 0x407e0)
354032                              ( src=0x100015e0 (GOFF 0x407f1)
354032                              | copy_nonoverlapping<core::option::Option<&mut [u8]>> (GOFF 0x40808)
354032                                ( dst=0x100015e0 (GOFF 0x40be2)
354032                                ( dst=0x100015e0 (GOFF 0x4085b)
354032                                ( count=0x1 (GOFF 0x40827)
354032                                ( src=0x100015e0 (GOFF 0x40819)
354034                            | copy_nonoverlapping<core::option::Option<&mut [u8]>> (GOFF 0x40845)
354034                              ( count=0x1 (GOFF 0x40864)
354034                              ( dst=0x100015e0 (GOFF 0x40be2)
354034                              ( dst=0x100015e0 (GOFF 0x4085b)
354034                              ( count=0x1 (GOFF 0x40827)
354034                              ( src=0x100015e0 (GOFF 0x40819)
354035                  | map<&mut [u8],(),closure-0> (GOFF 0x40882)
354035                    ( self=0x10001fe4 (GOFF 0x4088e)
354038                  | map<&mut [u8],(),closure-0> (GOFF 0x40882)
354038                    ( self=0x10001fe4 (GOFF 0x4088e), 0x40 (GOFF 0x4088e)
354038                    | {{closure}}<[u8],closure-0,()> (GOFF 0x408a5)
354038                      ( val=0x10001fe4 (GOFF 0x408b2), 0x40 (GOFF 0x408b2)
354038                      | {{closure}} (GOFF 0x408bb)
354038                        ( tx_buf=0x10001fe4 (GOFF 0x408c7), 0x40 (GOFF 0x408c7)
354041                        | next<usize> (GOFF 0x408e8)
354042                          | add_usize (GOFF 0x408f9)
354042                            ( n=0x1 (GOFF 0x4090e)
354042                            | checked_add (GOFF 0x40929)
354042                              ( rhs=0x1 (GOFF 0x40939)
354042                              | overflowing_add (GOFF 0x40942)
354042                                ( rhs=0x1 (GOFF 0x40953)
354052                        | is_set<u32,lowrisc::uart::status::Register> (GOFF 0x40961)
354052                          ( field=0x1 (GOFF 0x4097a), 0x0 (GOFF 0x4097a)
354052                          | read<u32,lowrisc::uart::status::Register> (GOFF 0x40983)
354052                            ( field=0x1 (GOFF 0x4099d), 0x0 (GOFF 0x4099d)
354052                            | get<u32,lowrisc::uart::status::Register> (GOFF 0x409a6)
354052                              | read_volatile<u32> (GOFF 0x409bf)
354053                            | bitand (GOFF 0x409da)
354053                              ( rhs=0x1 (GOFF 0x409f3)
354053                              ( self=0x0 (GOFF 0x409ea)
354067                        | write<u32,lowrisc::uart::wdata::Register> (GOFF 0x409ff)
354067                          ( field=0x5b (GOFF 0x40a18)
354067                          | set<u32,lowrisc::uart::wdata::Register> (GOFF 0x40a21)
354067                            ( value=0x5b (GOFF 0x40a3b)
354067                            | write_volatile<u32> (GOFF 0x40a44)
354067                              ( src=0x5b (GOFF 0x40a5e)
354068                        | set<usize> (GOFF 0x40a6a)
354068                          | replace<usize> (GOFF 0x40a83)
354068                            | replace<usize> (GOFF 0x40a9d)
354068                              | swap<usize> (GOFF 0x40abc)
354068                                | swap_nonoverlapping_one<usize> (GOFF 0x40ad6)
354068                                  | copy_nonoverlapping<usize> (GOFF 0x40afe)
354068                                    ( count=0x1 (GOFF 0x40b18)
354072                        | next<usize> (GOFF 0x408e8)
354074                      | replace<[u8]> (GOFF 0x40b2b)
354074                        | replace<core::option::Option<&mut [u8]>> (GOFF 0x40b4d)
354074                          | replace<core::option::Option<&mut [u8]>> (GOFF 0x40b6f)
354074                            | swap<core::option::Option<&mut [u8]>> (GOFF 0x40b8e)
354074                              | swap_nonoverlapping_one<core::option::Option<&mut [u8]>> (GOFF 0x40bad)
354074                                | copy_nonoverlapping<core::option::Option<&mut [u8]>> (GOFF 0x40bcc)
354085                | write<u32,lowrisc::uart::intr::Register> (GOFF 0x40bff)
354085                  ( field=0x4 (GOFF 0x40c18)
354085                  ( self=0x40000000 (GOFF 0x40c0f)
354085                  | set<u32,lowrisc::uart::intr::Register> (GOFF 0x40c21)
354085                    ( value=0x4 (GOFF 0x40c3a)
354085                    ( self=0x40000000 (GOFF 0x40c31)
354085                    | write_volatile<u32> (GOFF 0x40c43)
354085                      ( src=0x4 (GOFF 0x40c5c)
354085                      ( dst=0x40000000 (GOFF 0x40c53)
354093                | is_set<u32,lowrisc::uart::status::Register> (GOFF 0x40c68)
354093                  ( self=0x40000010 (GOFF 0x40e09)
354093                  ( field=0x1 (GOFF 0x40c81), 0x2 (GOFF 0x40c81)
354093                  ( self=0x40000010 (GOFF 0x40c78)
354093                  | read<u32,lowrisc::uart::status::Register> (GOFF 0x40c8a)
354093                    ( self=0x40000010 (GOFF 0x40e2c)
354093                    ( field=0x1 (GOFF 0x40ca4), 0x2 (GOFF 0x40ca4)
354093                    ( self=0x40000010 (GOFF 0x40c9b)
354093                    | get<u32,lowrisc::uart::status::Register> (GOFF 0x40cad)
354093                      ( self=0x40000010 (GOFF 0x40e4e)
354093                      ( self=0x40000010 (GOFF 0x40cbd)
354093                      | read_volatile<u32> (GOFF 0x40cc6)
354093                        ( src=0x40000010 (GOFF 0x40e67)
354093                        ( src=0x40000010 (GOFF 0x40cd6)
354094                    | shr (GOFF 0x40ce1)
354094                      ( other=0x2 (GOFF 0x40cfa)
354094                      ( self=0x4 (GOFF 0x40cf1)
354099                | get<usize> (GOFF 0x40ff3)
354101                | get<usize> (GOFF 0x4100d)
354104                | map<&TransmitClient,closure-1,()> (GOFF 0x41027)
354104                  ( self=0x100015d0 (GOFF 0x41037)
354104                  | get<core::option::Option<&TransmitClient>> (GOFF 0x41040)
354104                    ( self=0x100015d0 (GOFF 0x41050)
354105                  | map<&TransmitClient,(),closure-0> (GOFF 0x4105a)
354105                    ( self=0x10002108 (GOFF 0x41066)
354109                  | map<&TransmitClient,(),closure-0> (GOFF 0x4105a)
354109                    ( self=0x10002108 (GOFF 0x41066), 0x10001354 (GOFF 0x41066)
354109                    | {{closure}}<&TransmitClient,closure-1,()> (GOFF 0x41081)
354109                      | {{closure}} (GOFF 0x41097)
354109                        | take<[u8]> (GOFF 0x410ac)
354109                          ( self=0x100015e0 (GOFF 0x410bc)
354109                          | replace<core::option::Option<&mut [u8]>> (GOFF 0x410c5)
354109                            ( val=0x0 (GOFF 0x410de)
354109                            ( self=0x100015e0 (GOFF 0x410d5)
354109                            | replace<core::option::Option<&mut [u8]>> (GOFF 0x410e7)
354109                              ( dest=0x100015e0 (GOFF 0x410f8)
354109                              | swap<core::option::Option<&mut [u8]>> (GOFF 0x41106)
354109                                ( x=0x100015e0 (GOFF 0x41117)
354109                                | swap_nonoverlapping_one<core::option::Option<&mut [u8]>> (GOFF 0x41125)
354109                                  ( x=0x100015e0 (GOFF 0x41136)
354109                                  | read<core::option::Option<&mut [u8]>> (GOFF 0x41144)
354109                                    ( src=0x100015e0 (GOFF 0x41155)
354109                                    | copy_nonoverlapping<core::option::Option<&mut [u8]>> (GOFF 0x4116c)
354109                                      ( dst=0x100015e0 (GOFF 0x411bf)
354109                                      ( count=0x1 (GOFF 0x4118b)
354109                                      ( src=0x100015e0 (GOFF 0x4117d)
354111                                  | copy_nonoverlapping<core::option::Option<&mut [u8]>> (GOFF 0x411a9)
354111                                    ( count=0x1 (GOFF 0x411c8)
354111                                    ( dst=0x100015e0 (GOFF 0x411bf)
354111                                    ( count=0x1 (GOFF 0x4118b)
354111                                    ( src=0x100015e0 (GOFF 0x4117d)
354112                        | map<&mut [u8],(),closure-0> (GOFF 0x411d8)
354112                          ( self=0x10001fe4 (GOFF 0x411e4)
354116                        | map<&mut [u8],(),closure-0> (GOFF 0x411d8)
354116                          ( self=0x10001fe4 (GOFF 0x411e4), 0x40 (GOFF 0x411e4)
354116                          | {{closure}} (GOFF 0x41204)
354116                            ( tx_buf=0x40 (GOFF 0x41215)
354121                     -> transmitted_buffer (GOFF 0x11c7a)
354121                        ( rcode=0x1 (GOFF 0x11cbc), 0x200026fe (GOFF 0x11cbc)
354121                        ( tx_len=0x1 (GOFF 0x11cad)
354121                        ( tx_buffer=0x10001fe4 (GOFF 0x11c9e), 0x40 (GOFF 0x11c9e)
354121                        ( self=0x10002108 (GOFF 0x11c8f)
354133                        | map<&capsules::virtual_uart::UartDevice,closure-0,()> (GOFF 0x11ccb)
354133                          ( self=0x10002118 (GOFF 0x11cdb)
354133                          | get<core::option::Option<&capsules::virtual_uart::UartDevice>> (GOFF 0x11ce4)
354133                            ( self=0x10002118 (GOFF 0x11cf4)
354135                          | map<&capsules::virtual_uart::UartDevice,(),closure-0> (GOFF 0x11cfe)
354135                            ( f=0x10002108 (GOFF 0x11d17)
354135                            ( self=0x0 (GOFF 0x11d0e)
354164                        | is_none<&capsules::virtual_uart::UartDevice> (GOFF 0x1432b)
354164                          ( self=0x10002118 (GOFF 0x1433b)
354164                          | get<core::option::Option<&capsules::virtual_uart::UartDevice>> (GOFF 0x14344)
354164                            ( self=0x10002118 (GOFF 0x14354)
354170                        | find<kernel::common::list::ListIterator<capsules::virtual_uart::UartDevice>,closure-0> (GOFF 0x1435f)
354170                          | try_fold<kernel::common::list::ListIterator<capsules::virtual_uart::UartDevice>,(),closure-0,core::iter::LoopState<(), &capsules::virtual_uart::UartDevice>> (GOFF 0x14374)
354170                            | next<capsules::virtual_uart::UartDevice> (GOFF 0x1438a)
354171                              | next (GOFF 0x143b2)
354171                                ( self=0x10002064 (GOFF 0x143c2)
354173                            | {{closure}}<&capsules::virtual_uart::UartDevice,closure-0> (GOFF 0x143e0)
354173                              ( x=0x10002064 (GOFF 0x143f1)
354173                              | {{closure}} (GOFF 0x143fa)
354173                                | is_some<capsules::virtual_uart::Operation> (GOFF 0x14410)
354173                                  | get<core::option::Option<capsules::virtual_uart::Operation>> (GOFF 0x14420)
354179                        | find<kernel::common::list::ListIterator<capsules::virtual_uart::UartDevice>,closure-0> (GOFF 0x1435f)
354179                          | try_fold<kernel::common::list::ListIterator<capsules::virtual_uart::UartDevice>,(),closure-0,core::iter::LoopState<(), &capsules::virtual_uart::UartDevice>> (GOFF 0x14374)
354179                            | next<capsules::virtual_uart::UartDevice> (GOFF 0x1438a)
354180                              | next (GOFF 0x143b2)
354180                                ( self=0x100025a8 (GOFF 0x143c2)
354182                            | {{closure}}<&capsules::virtual_uart::UartDevice,closure-0> (GOFF 0x143e0)
354182                              ( x=0x100025a8 (GOFF 0x143f1)
354182                              | {{closure}} (GOFF 0x143fa)
354182                                | is_some<capsules::virtual_uart::Operation> (GOFF 0x14410)
354182                                  | get<core::option::Option<capsules::virtual_uart::Operation>> (GOFF 0x14420)
354188                        | find<kernel::common::list::ListIterator<capsules::virtual_uart::UartDevice>,closure-0> (GOFF 0x1435f)
354188                          | try_fold<kernel::common::list::ListIterator<capsules::virtual_uart::UartDevice>,(),closure-0,core::iter::LoopState<(), &capsules::virtual_uart::UartDevice>> (GOFF 0x14374)
354188                            | next<capsules::virtual_uart::UartDevice> (GOFF 0x1438a)
354189                              | next (GOFF 0x143b2)
354189                                ( self=0x10002134 (GOFF 0x143c2)
354191                            | {{closure}}<&capsules::virtual_uart::UartDevice,closure-0> (GOFF 0x143e0)
354191                              ( x=0x10002134 (GOFF 0x143f1)
354191                              | {{closure}} (GOFF 0x143fa)
354191                                | is_some<capsules::virtual_uart::Operation> (GOFF 0x14410)
354191                                  | get<core::option::Option<capsules::virtual_uart::Operation>> (GOFF 0x14420)
354194                        | map<&capsules::virtual_uart::UartDevice,(),closure-1> (GOFF 0x1444a)
354194                          ( self=0x10002134 (GOFF 0x14456)
354194                          | {{closure}} (GOFF 0x14472)
354194                            ( node=0x10002134 (GOFF 0x1447f)
354194                            | take<[u8]> (GOFF 0x1448d)
354194                              ( self=0x10002138 (GOFF 0x1449d)
354194                              | replace<core::option::Option<&mut [u8]>> (GOFF 0x144a6)
354194                                ( val=0x0 (GOFF 0x144bf)
354194                                ( self=0x10002138 (GOFF 0x144b6)
354194                                | replace<core::option::Option<&mut [u8]>> (GOFF 0x144c8)
354194                                  ( dest=0x10002138 (GOFF 0x144d9)
354194                                  | swap<core::option::Option<&mut [u8]>> (GOFF 0x144e7)
354194                                    ( x=0x10002138 (GOFF 0x144f8)
354194                                    | swap_nonoverlapping_one<core::option::Option<&mut [u8]>> (GOFF 0x14506)
354194                                      ( x=0x10002138 (GOFF 0x14517)
354194                                      | read<core::option::Option<&mut [u8]>> (GOFF 0x14525)
354194                                        ( src=0x10002138 (GOFF 0x14536)
354194                                        | copy_nonoverlapping<core::option::Option<&mut [u8]>> (GOFF 0x1454d)
354194                                          ( dst=0x10002138 (GOFF 0x145a0)
354194                                          ( count=0x1 (GOFF 0x1456c)
354194                                          ( src=0x10002138 (GOFF 0x1455e)
354196                                      | copy_nonoverlapping<core::option::Option<&mut [u8]>> (GOFF 0x1458a)
354196                                        ( count=0x1 (GOFF 0x145a9)
354196                                        ( dst=0x10002138 (GOFF 0x145a0)
354196                                        ( count=0x1 (GOFF 0x1456c)
354196                                        ( src=0x10002138 (GOFF 0x1455e)
354198                            | map<&mut [u8],(),closure-0> (GOFF 0x145b9)
354198                              ( self=0x0 (GOFF 0x145c5)
354202                            | clear<capsules::virtual_uart::Operation> (GOFF 0x14991)
354202                              | set<core::option::Option<capsules::virtual_uart::Operation>> (GOFF 0x149aa)
354202                                ( val=0x2 (GOFF 0x149c3)
354202                                | replace<core::option::Option<capsules::virtual_uart::Operation>> (GOFF 0x149cc)
354202                                  ( val=0x2 (GOFF 0x149e6)
354202                                  | replace<core::option::Option<capsules::virtual_uart::Operation>> (GOFF 0x149ef)
354202                                    | swap<core::option::Option<capsules::virtual_uart::Operation>> (GOFF 0x14a0e)
354202                                      | swap_nonoverlapping_one<core::option::Option<capsules::virtual_uart::Operation>> (GOFF 0x14a2d)
354202                                        | copy_nonoverlapping<core::option::Option<capsules::virtual_uart::Operation>> (GOFF 0x14a5a)
354202                                          ( count=0x1 (GOFF 0x14a79)
354207                            | set<&capsules::virtual_uart::UartDevice> (GOFF 0x14a8a)
354207                              | set<core::option::Option<&capsules::virtual_uart::UartDevice>> (GOFF 0x14aac)
354207                                | replace<core::option::Option<&capsules::virtual_uart::UartDevice>> (GOFF 0x14ace)
354207                                  | replace<core::option::Option<&capsules::virtual_uart::UartDevice>> (GOFF 0x14af1)
354207                                    | swap<core::option::Option<&capsules::virtual_uart::UartDevice>> (GOFF 0x14b14)
354207                                      | swap_nonoverlapping_one<core::option::Option<&capsules::virtual_uart::UartDevice>> (GOFF 0x14b33)
354207                                        | copy_nonoverlapping<core::option::Option<&capsules::virtual_uart::UartDevice>> (GOFF 0x14b52)
354207                                          ( count=0x1 (GOFF 0x14b71)
354220                     <- do_next_op (GOFF 0x14307)
354227             <- tx_progress (GOFF 0x406bd)
354241           <- transmit_buffer (GOFF 0x418ab)
354244    | map<&capsules::virtual_uart::UartDevice,(),closure-1> (GOFF 0x1444a)
354244      ( self=0x10002134 (GOFF 0x14456)
354244      | {{closure}} (GOFF 0x14472)
354244        ( node=0x10002134 (GOFF 0x1447f)
354244        | map<&mut [u8],(),closure-0> (GOFF 0x145b9)
354244          | {{closure}} (GOFF 0x145e1)
354244            | map<capsules::virtual_uart::Operation,closure-0,()> (GOFF 0x145fc)
354244              | map<capsules::virtual_uart::Operation,(),closure-0> (GOFF 0x1462b)
354244                | {{closure}}<capsules::virtual_uart::Operation,closure-0,()> (GOFF 0x1464e)
354244                  | {{closure}} (GOFF 0x14660)
354260        | clear<capsules::virtual_uart::Operation> (GOFF 0x14991)
354260          | set<core::option::Option<capsules::virtual_uart::Operation>> (GOFF 0x149aa)
354260            ( val=0x2 (GOFF 0x149c3)
354260            | replace<core::option::Option<capsules::virtual_uart::Operation>> (GOFF 0x149cc)
354260              ( val=0x2 (GOFF 0x149e6)
354260              | replace<core::option::Option<capsules::virtual_uart::Operation>> (GOFF 0x149ef)
354260                | swap<core::option::Option<capsules::virtual_uart::Operation>> (GOFF 0x14a0e)
354260                  | swap_nonoverlapping_one<core::option::Option<capsules::virtual_uart::Operation>> (GOFF 0x14a2d)
354260                    | copy_nonoverlapping<core::option::Option<capsules::virtual_uart::Operation>> (GOFF 0x14a5a)
354260                      ( count=0x1 (GOFF 0x14a79)
354265        | set<&capsules::virtual_uart::UartDevice> (GOFF 0x14a8a)
354265          | set<core::option::Option<&capsules::virtual_uart::UartDevice>> (GOFF 0x14aac)
354265            | replace<core::option::Option<&capsules::virtual_uart::UartDevice>> (GOFF 0x14ace)
354265              | replace<core::option::Option<&capsules::virtual_uart::UartDevice>> (GOFF 0x14af1)
354265                | swap<core::option::Option<&capsules::virtual_uart::UartDevice>> (GOFF 0x14b14)
354265                  | swap_nonoverlapping_one<core::option::Option<&capsules::virtual_uart::UartDevice>> (GOFF 0x14b33)
354265                    | copy_nonoverlapping<core::option::Option<&capsules::virtual_uart::UartDevice>> (GOFF 0x14b52)
354265                      ( count=0x1 (GOFF 0x14b71)
354278 <- do_next_op (GOFF 0x14307)
354279    | call_global_instance_while<closure-0> (GOFF 0x57a07)
354279      | map<&kernel::common::dynamic_deferred_call::DynamicDeferredCall,(),closure-0> (GOFF 0x57a18)
354279        | {{closure}}<closure-0> (GOFF 0x57a43)
354279          | call_while<closure-0> (GOFF 0x57a5d)
354281            | next<core::slice::Iter<kernel::common::dynamic_deferred_call::DynamicDeferredCallClientState>> (GOFF 0x57aa8)
354281              | next<kernel::common::dynamic_deferred_call::DynamicDeferredCallClientState> (GOFF 0x57ab9)
354286            | any<core::slice::Iter<kernel::common::dynamic_deferred_call::DynamicDeferredCallClientState>,closure-1> (GOFF 0x57cbb)
354286              | try_fold<core::slice::Iter<kernel::common::dynamic_deferred_call::DynamicDeferredCallClientState>,(),closure-0,core::iter::LoopState<(), ()>> (GOFF 0x57ccc)
354293            | any<core::slice::Iter<kernel::common::dynamic_deferred_call::DynamicDeferredCallClientState>,closure-1> (GOFF 0x57cbb)
354293              | try_fold<core::slice::Iter<kernel::common::dynamic_deferred_call::DynamicDeferredCallClientState>,(),closure-0,core::iter::LoopState<(), ()>> (GOFF 0x57ccc)
354293                | next<kernel::common::dynamic_deferred_call::DynamicDeferredCallClientState> (GOFF 0x57cde)
354295                | next<kernel::common::dynamic_deferred_call::DynamicDeferredCallClientState> (GOFF 0x57cde)
354295                  | post_inc_start<kernel::common::dynamic_deferred_call::DynamicDeferredCallClientState> (GOFF 0x57cf0)
354295                    ( offset=0x1 (GOFF 0x57d06)
354295                    | new_unchecked<kernel::common::dynamic_deferred_call::DynamicDeferredCallClientState> (GOFF 0x57d21)
354295                      ( ptr=0x1000266c (GOFF 0x57d32)
354297                | {{closure}}<&kernel::common::dynamic_deferred_call::DynamicDeferredCallClientState,closure-1> (GOFF 0x57d4d)
354297                  ( x=0x10002660 (GOFF 0x57d5e)
354297                  | {{closure}}<closure-0> (GOFF 0x57d67)
354297                    ( client_state=0x10002660 (GOFF 0x57d7d)
354297                    | get<bool> (GOFF 0x57d86)
354297                      ( self=0x10002668 (GOFF 0x57d96)
354298                | next<kernel::common::dynamic_deferred_call::DynamicDeferredCallClientState> (GOFF 0x57cde)
354303                | next<kernel::common::dynamic_deferred_call::DynamicDeferredCallClientState> (GOFF 0x57cde)
354305            | set<bool> (GOFF 0x57da5)
354314    | iter<core::option::Option<&ProcessType>> (GOFF 0x57dbb)
354314      ( self=0x10002628 (GOFF 0x57dcc), 0x20 (GOFF 0x57dcc)
354314      | add<core::option::Option<&ProcessType>> (GOFF 0x57de7)
354314        ( count=0x20 (GOFF 0x57e01)
354314        ( self=0x10002628 (GOFF 0x57df8)
354314        | offset<core::option::Option<&ProcessType>> (GOFF 0x57e0a)
354314          ( count=0x20 (GOFF 0x57e24)
354314          ( self=0x10002628 (GOFF 0x57e1b)
354321    | next<core::option::Option<&ProcessType>> (GOFF 0x57e57)
354335    | map<&ProcessType,(),closure-1> (GOFF 0x57e83)
354335      ( self=0x10002614 (GOFF 0x57e90)
354335      | {{closure}}<opentitan::OpenTitan,ibex::chip::Ibex> (GOFF 0x57ea7)
354335        ( process=0x10002614 (GOFF 0x57eb4)
354335        | do_process<opentitan::OpenTitan,ibex::chip::Ibex> (GOFF 0x57ec2)
354335          | with_driver<closure-3,kernel::returncode::ReturnCode> (GOFF 0x57f35)
354356          | has_pending_interrupts (GOFF 0x5b2b5)
354356            | extract<u32,rv32i::csr::mip::mip::Register> (GOFF 0x5b2cb)
354356              | get<u32,rv32i::csr::mip::mip::Register> (GOFF 0x5b2db)
354358            | matches_any<u32,rv32i::csr::mip::mip::Register> (GOFF 0x5b312)
354358              ( field=0x880 (GOFF 0x5b322)
354358              | bitand (GOFF 0x5b32b)
354358                ( rhs=0x880 (GOFF 0x5b345)
354358                ( self=0x0 (GOFF 0x5b33c)
354369      -> get_state<ibex::chip::Ibex> (GOFF 0x7db4)
354369         ( self=0x10004850 (GOFF 0x7dce)
354383      <- get_state<ibex::chip::Ibex> (GOFF 0x7db4)
354385    | map<&ProcessType,(),closure-1> (GOFF 0x57e83)
354385      | {{closure}}<opentitan::OpenTitan,ibex::chip::Ibex> (GOFF 0x57ea7)
354385        | do_process<opentitan::OpenTitan,ibex::chip::Ibex> (GOFF 0x57ec2)
354385          ( ipc=0x0 (GOFF 0x57edd)
354385          ( platform=0x10000fa8 (GOFF 0x57ed4)
354402      -> dequeue_task<ibex::chip::Ibex> (GOFF 0x262c)
354402         ( self=0x10004850 (GOFF 0x2646)
354422         | map_or<kernel::common::ring_buffer::RingBuffer<kernel::process::Task>,closure-0,core::option::Option<kernel::process::Task>> (GOFF 0x2656)
354422           | map<kernel::common::ring_buffer::RingBuffer<kernel::process::Task>,closure-0,core::option::Option<kernel::process::Task>> (GOFF 0x266c)
354422             | is_some<kernel::common::ring_buffer::RingBuffer<kernel::process::Task>> (GOFF 0x2681)
354422               | get<bool> (GOFF 0x269a)
354426             | set<bool> (GOFF 0x26b5)
354426               ( self=0x10004a40 (GOFF 0x295c)
354426               ( val=0x0 (GOFF 0x26ce)
354426               ( self=0x10004a40 (GOFF 0x26c5)
354426               | replace<bool> (GOFF 0x26d7)
354426                 ( self=0x10004a40 (GOFF 0x297f)
354426                 ( val=0x0 (GOFF 0x26f1)
354426                 ( self=0x10004a40 (GOFF 0x26e8)
354426                 | replace<bool> (GOFF 0x26fa)
354426                   ( dest=0x10004a40 (GOFF 0x29a2)
354426                   ( src=0x0 (GOFF 0x2714)
354426                   ( dest=0x10004a40 (GOFF 0x270b)
354426                   | swap<bool> (GOFF 0x271d)
354426                     ( x=0x10004a40 (GOFF 0x29c5)
354426                     ( x=0x10004a40 (GOFF 0x272e)
354426                     | swap_nonoverlapping_one<bool> (GOFF 0x2737)
354426                       ( x=0x10004a40 (GOFF 0x29df)
354426                       ( x=0x10004a40 (GOFF 0x2748)
354426                       | copy_nonoverlapping<bool> (GOFF 0x2751)
354426                         ( dst=0x10004a40 (GOFF 0x29f9)
354426                         ( count=0x1 (GOFF 0x276b)
354426                         ( dst=0x10004a40 (GOFF 0x2762)
354429             | {{closure}}<ibex::chip::Ibex> (GOFF 0x2788)
354429               ( tasks=0x10004a30 (GOFF 0x2794)
354429               | dequeue<kernel::process::Task> (GOFF 0x279d)
354429                 ( self=0x10004a30 (GOFF 0x27aa)
354429                 | has_elements<kernel::process::Task> (GOFF 0x27b3)
354429                   ( self=0x10004a30 (GOFF 0x27c3)
354438             | set<bool> (GOFF 0x294c)
354438               ( val=0xffffffff (GOFF 0x2965)
354438               | replace<bool> (GOFF 0x296e)
354438                 ( val=0xffffffff (GOFF 0x2988)
354438                 | replace<bool> (GOFF 0x2991)
354438                   ( src=0x1 (GOFF 0x29ab)
354438                   | swap<bool> (GOFF 0x29b4)
354438                     | swap_nonoverlapping_one<bool> (GOFF 0x29ce)
354438                       | copy_nonoverlapping<bool> (GOFF 0x29e8)
354438                         ( count=0x1 (GOFF 0x2a02)
354447          -> memcpy (GOFF 0x725ae)
354810          <- memcpy (GOFF 0x725ae)
354813         | map_or<kernel::common::ring_buffer::RingBuffer<kernel::process::Task>,closure-0,core::option::Option<kernel::process::Task>> (GOFF 0x2656)
354821         -> memcpy (GOFF 0x725ae)
355184         <- memcpy (GOFF 0x725ae)
355187         | map_or<kernel::common::ring_buffer::RingBuffer<kernel::process::Task>,closure-0,core::option::Option<kernel::process::Task>> (GOFF 0x2656)
355187           | unwrap_or<core::option::Option<kernel::process::Task>> (GOFF 0x2a14)
355187             ( self=0x0 (GOFF 0x2a20)
355196          -> memcpy (GOFF 0x725ae)
355559          <- memcpy (GOFF 0x725ae)
355578      <- dequeue_task<ibex::chip::Ibex> (GOFF 0x262c)
355580    | map<&ProcessType,(),closure-1> (GOFF 0x57e83)
355580      | {{closure}}<opentitan::OpenTitan,ibex::chip::Ibex> (GOFF 0x57ea7)
355580        | do_process<opentitan::OpenTitan,ibex::chip::Ibex> (GOFF 0x57ec2)
355580          ( ipc=0x0 (GOFF 0x57edd)
355580          ( platform=0x10000fa8 (GOFF 0x57ed4)
355588    | has_pending_interrupts (GOFF 0x5b3b4)
355588      | extract<u32,rv32i::csr::mip::mip::Register> (GOFF 0x5b3ca)
355588        | get<u32,rv32i::csr::mip::mip::Register> (GOFF 0x5b3da)
355590      | matches_any<u32,rv32i::csr::mip::mip::Register> (GOFF 0x5b411)
355590        ( field=0x880 (GOFF 0x5b421)
355590        | bitand (GOFF 0x5b42a)
355590          ( rhs=0x880 (GOFF 0x5b444)
355590          ( self=0x0 (GOFF 0x5b43b)
355594    | global_instance_calls_pending (GOFF 0x5b451)
355595      | map<&kernel::common::dynamic_deferred_call::DynamicDeferredCall,bool,closure-0> (GOFF 0x5b462)
355595        ( self=0x1000266c (GOFF 0x5b472)
355597        | {{closure}} (GOFF 0x5b48d)
355597          ( ddc=0x0 (GOFF 0x5b49e)
355597          | has_pending (GOFF 0x5b4a7)
355597            ( self=0x0 (GOFF 0x5b4b7)
355597            | get<bool> (GOFF 0x5b4c0)
355597              ( self=0xc (GOFF 0x5b4d0)
355608    | next<core::option::Option<&ProcessType>> (GOFF 0x57e57)
355618    | has_pending_interrupts (GOFF 0x5b3b4)
355618      | extract<u32,rv32i::csr::mip::mip::Register> (GOFF 0x5b3ca)
355618        | get<u32,rv32i::csr::mip::mip::Register> (GOFF 0x5b3da)
355620      | matches_any<u32,rv32i::csr::mip::mip::Register> (GOFF 0x5b411)
355620        ( field=0x880 (GOFF 0x5b421)
355620        | bitand (GOFF 0x5b42a)
355620          ( rhs=0x880 (GOFF 0x5b444)
355620          ( self=0x0 (GOFF 0x5b43b)
355624    | global_instance_calls_pending (GOFF 0x5b451)
355625      | map<&kernel::common::dynamic_deferred_call::DynamicDeferredCall,bool,closure-0> (GOFF 0x5b462)
355625        ( self=0x1000266c (GOFF 0x5b472)
355627        | {{closure}} (GOFF 0x5b48d)
355627          ( ddc=0x0 (GOFF 0x5b49e)
355627          | has_pending (GOFF 0x5b4a7)
355627            ( self=0x0 (GOFF 0x5b4b7)
355627            | get<bool> (GOFF 0x5b4c0)
355627              ( self=0xc (GOFF 0x5b4d0)
355638    | next<core::option::Option<&ProcessType>> (GOFF 0x57e57)
355648    | has_pending_interrupts (GOFF 0x5b3b4)
355648      | extract<u32,rv32i::csr::mip::mip::Register> (GOFF 0x5b3ca)
355648        | get<u32,rv32i::csr::mip::mip::Register> (GOFF 0x5b3da)
355650      | matches_any<u32,rv32i::csr::mip::mip::Register> (GOFF 0x5b411)
355650        ( field=0x880 (GOFF 0x5b421)
355650        | bitand (GOFF 0x5b42a)
355650          ( rhs=0x880 (GOFF 0x5b444)
355650          ( self=0x0 (GOFF 0x5b43b)
355654    | global_instance_calls_pending (GOFF 0x5b451)
355655      | map<&kernel::common::dynamic_deferred_call::DynamicDeferredCall,bool,closure-0> (GOFF 0x5b462)
355655        ( self=0x1000266c (GOFF 0x5b472)
355657        | {{closure}} (GOFF 0x5b48d)
355657          ( ddc=0x0 (GOFF 0x5b49e)
355657          | has_pending (GOFF 0x5b4a7)
355657            ( self=0x0 (GOFF 0x5b4b7)
355657            | get<bool> (GOFF 0x5b4c0)
355657              ( self=0xc (GOFF 0x5b4d0)
355668    | next<core::option::Option<&ProcessType>> (GOFF 0x57e57)
355678    | has_pending_interrupts (GOFF 0x5b3b4)
355678      | extract<u32,rv32i::csr::mip::mip::Register> (GOFF 0x5b3ca)
355678        | get<u32,rv32i::csr::mip::mip::Register> (GOFF 0x5b3da)
355680      | matches_any<u32,rv32i::csr::mip::mip::Register> (GOFF 0x5b411)
355680        ( field=0x880 (GOFF 0x5b421)
355680        | bitand (GOFF 0x5b42a)
355680          ( rhs=0x880 (GOFF 0x5b444)
355680          ( self=0x0 (GOFF 0x5b43b)
355684    | global_instance_calls_pending (GOFF 0x5b451)
355685      | map<&kernel::common::dynamic_deferred_call::DynamicDeferredCall,bool,closure-0> (GOFF 0x5b462)
355685        ( self=0x1000266c (GOFF 0x5b472)
355687        | {{closure}} (GOFF 0x5b48d)
355687          ( ddc=0x0 (GOFF 0x5b49e)
355687          | has_pending (GOFF 0x5b4a7)
355687            ( self=0x0 (GOFF 0x5b4b7)
355687            | get<bool> (GOFF 0x5b4c0)
355687              ( self=0xc (GOFF 0x5b4d0)
355698    | next<core::option::Option<&ProcessType>> (GOFF 0x57e57)
355703    | atomic<closure-2,()> (GOFF 0x5b4e2)
355703      | atomic<closure-2,()> (GOFF 0x5b4f8)
355703        | extract<u32,rv32i::csr::mstatus::mstatus::Register> (GOFF 0x5b508)
355703          | get<u32,rv32i::csr::mstatus::mstatus::Register> (GOFF 0x5b518)
355704        | is_set<u32,rv32i::csr::mstatus::mstatus::Register> (GOFF 0x5b54f)
355704          ( field=0x1 (GOFF 0x5b55f), 0x3 (GOFF 0x5b55f)
355704          | read<u32,rv32i::csr::mstatus::mstatus::Register> (GOFF 0x5b568)
355704            ( field=0x1 (GOFF 0x5b579), 0x3 (GOFF 0x5b579)
355704            | shr (GOFF 0x5b582)
355704              ( other=0x3 (GOFF 0x5b59c)
355704              ( self=0x8 (GOFF 0x5b593)
355706        | modify_no_read<u32,rv32i::csr::mstatus::mstatus::Register> (GOFF 0x5b5a8)
355706          ( field=0x8 (GOFF 0x5b5c1), 0x0 (GOFF 0x5b5c1)
355706          ( original=0x88 (GOFF 0x5b5b8)
355706          | bitand (GOFF 0x5b5ca)
355706            ( rhs=0xfffffff7 (GOFF 0x5b5e3)
355706            ( self=0x88 (GOFF 0x5b5da)
355707          | set<u32,rv32i::csr::mstatus::mstatus::Register> (GOFF 0x5b5ed)
355707            ( val_to_set=0x80 (GOFF 0x5b5fd)
355710        | {{closure}}<opentitan::OpenTitan,ibex::chip::Ibex> (GOFF 0x5b608)
355710          | has_pending_interrupts (GOFF 0x5b614)
355710            | extract<u32,rv32i::csr::mip::mip::Register> (GOFF 0x5b62a)
355710              | get<u32,rv32i::csr::mip::mip::Register> (GOFF 0x5b63a)
355712            | matches_any<u32,rv32i::csr::mip::mip::Register> (GOFF 0x5b671)
355712              ( field=0x880 (GOFF 0x5b681)
355712              | bitand (GOFF 0x5b68a)
355712                ( rhs=0x880 (GOFF 0x5b6a4)
355712                ( self=0x0 (GOFF 0x5b69b)
355719          | global_instance_calls_pending (GOFF 0x5b6b1)
355720            | map<&kernel::common::dynamic_deferred_call::DynamicDeferredCall,bool,closure-0> (GOFF 0x5b6c2)
355720              ( self=0x1000266c (GOFF 0x5b6d2)
355722              | {{closure}} (GOFF 0x5b6ed)
355722                ( ddc=0x0 (GOFF 0x5b6fe)
355722                | has_pending (GOFF 0x5b707)
355722                  ( self=0x0 (GOFF 0x5b717)
355722                  | get<bool> (GOFF 0x5b720)
355722                    ( self=0xc (GOFF 0x5b730)
355724          | processes_blocked (GOFF 0x5b73f)
355724            | get<usize> (GOFF 0x5b755)
355728          | sleep (GOFF 0x5b76c)
355728            | wfi (GOFF 0x5b782)

The actual write of the one character we see ('[', 0x5b) occurs at cycle 354067. The call that begins this output is GOFF 0x13437. We can look at the Gimli dwarfdump -G output of the opentitan.elf binary to better understand this:

...
< 4><0x00001992 GOFF=0x00013437>          DW_TAG_subprogram
                                            DW_AT_low_pc                0x20002a10
                                            DW_AT_high_pc               <offset-from-lowpc>22
                                            DW_AT_frame_base            len 0x0001: 58: DW_OP_reg8
                                            DW_AT_linkage_name          _ZN116_$LT$capsules..virtual_uart..MuxUart$u20$as$u20$kernel..common..dynamic_deferred_call..DynamicDeferredCallClient$GT$4call17hce6704ccc8611324E
                                            DW_AT_name                  call
                                            DW_AT_decl_file             0x00000003 tock/capsules/tock/capsules/src/virtual_uart.rs
                                            DW_AT_decl_line             0x0000010f
                                            DW_AT_external              yes
...

0x10f is 270, and corresponds to this code:

impl<'a> DynamicDeferredCallClient for MuxUart<'a> {
fn call(&self, _handle: DeferredCallHandle) {
self.do_next_op();
}
}

Which is to say, this code:

fn do_next_op(&self) {
if self.inflight.is_none() {
let mnode = self.devices.iter().find(|node| node.operation.is_some());
mnode.map(|node| {
node.tx_buffer.take().map(|buf| {
node.operation.map(move |op| match op {
Operation::Transmit { len } => {
let (rcode, rbuf) = self.uart.transmit_buffer(buf, *len);
if rcode != ReturnCode::SUCCESS {
node.tx_client.map(|client| {
node.transmitting.set(false);
client.transmitted_buffer(rbuf.unwrap(), 0, rcode);
});
}
}
Operation::TransmitWord { word } => {
let rcode = self.uart.transmit_word(*word);
if rcode != ReturnCode::SUCCESS {
node.tx_client.map(|client| {
node.transmitting.set(false);
client.transmitted_word(rcode);
});
}
}
});
});
node.operation.clear();
self.inflight.set(node);
});
}
}

In the output above, we can see that the call to transmit_buffer on line 202 is at cycle 353991. This kicks off the actual write (again, at cycle 354067). Because it's a one-byte write, this brings us into this code at cycle 354099:

if self.tx_index.get() == self.tx_len.get() {
self.tx_client.map(|client| {
self.tx_buffer.take().map(|tx_buf| {
client.transmitted_buffer(tx_buf, self.tx_len.get(), ReturnCode::SUCCESS);
});
});
}

This takes into this code at cycle 354121:

impl<'a> uart::TransmitClient for MuxUart<'a> {
fn transmitted_buffer(&self, tx_buffer: &'static mut [u8], tx_len: usize, rcode: ReturnCode) {
self.inflight.map(move |device| {
self.inflight.clear();
device.transmitted_buffer(tx_buffer, tx_len, rcode);
});
self.do_next_op();
}
}

From the trace, it's clear that we call immediately back into do_next_op -- we do not in fact call the transmitted_buffer callback that will allow our application to execute on its subscription. And from the code and trace, the reason is also clear: self.inflight hasn't (yet) been set, so the callback won't be called. This is the problem: in do_next_op, self.inflight is set after the call to transmit over the UART, when it should actually be set before the call to assure that transmitted_buffer properly propagates it.

In prototyping a fix for this, it became clear that the code suffers from a second problem, that will have similar symptoms: in addition to setting self.inflight in the wrong place, do_next_op also sets it unconditionally with respect to tx_buffer -- meaning that it in addition to not setting self.inflight when there is in fact data in-flight, it can also set self.inflight when there is nothing in-flight.

Here is a fix that fixes both problems:

--- a/capsules/src/virtual_uart.rs
+++ b/capsules/src/virtual_uart.rs
@@ -196,7 +196,15 @@ impl<'a> MuxUart<'a> {
         if self.inflight.is_none() {
             let mnode = self.devices.iter().find(|node| node.operation.is_some());
             mnode.map(|node| {
+
                 node.tx_buffer.take().map(|buf| {
+
+                    // Before we actually transmit, we need to set this node
+                    // as in-flight: transmission can complete synchronously --
+                    // and therefore call back into transmitted_buffer(), which
+                    // will call back into the client iff inflight is set.
+                    self.inflight.set(node);
+
                     node.operation.map(move |op| match op {
                         Operation::Transmit { len } => {
                             let (rcode, rbuf) = self.uart.transmit_buffer(buf, *len);
@@ -219,7 +227,6 @@ impl<'a> MuxUart<'a> {
                     });
                 });
                 node.operation.clear();
-                self.inflight.set(node);
             });
         }
     }

With this fix, the libtock-rs timer example runs correctly:

Version:    opentitan-snapshot-20191101-1-887-ge849d530
Build Date: 2020-04-03, 11:01:10
I00000 boot_rom.c:44] Boot ROM initialisation has completed, jump into flash!
OpenTitan initialisation complete. Entering main loop
[0] Waited roughly 0.000s. Now is 0.304s = 0x00000be5 ticks (None ticks since last time at 10000 Hz)
[1] Waited roughly 0.500s. Now is 6.438s = 0x0000fb7c ticks (Some(61335) ticks since last time at 10000 Hz)
[2] Waited roughly 1.000s. Now is 12.582s = 0x0001eb7e ticks (Some(61442) ticks since last time at 10000 Hz)
[3] Waited roughly 1.500s. Now is 18.922s = 0x0002e326 ticks (Some(63400) ticks since last time at 10000 Hz)
[4] Waited roughly 2.000s. Now is 24.875s = 0x0003cbb7 ticks (Some(59537) ticks since last time at 10000 Hz)
[5] Waited roughly 2.500s. Now is 31.215s = 0x0004c35f ticks (Some(63400) ticks since last time at 10000 Hz)
...
@gendx
Copy link
Contributor

gendx commented Apr 6, 2020

Nice catch and detailed investigation! I wonder if you've encountered #1651 or #1697 as well?

I think we really need better ways of avoiding these kinds of scheduling logic errors (other example: #1636), maybe with better abstractions and/or unit tests?

@gendx
Copy link
Contributor

gendx commented Apr 6, 2020

Btw, the frequency seem off on your platform.

OpenTitan initialisation complete. Entering main loop
[0] Waited roughly 0.000s. Now is 0.304s = 0x00000be5 ticks (None ticks since last time at 10000 Hz)
[1] Waited roughly 0.500s. Now is 6.438s = 0x0000fb7c ticks (Some(61335) ticks since last time at 10000 Hz)
[2] Waited roughly 1.000s. Now is 12.582s = 0x0001eb7e ticks (Some(61442) ticks since last time at 10000 Hz)
[3] Waited roughly 1.500s. Now is 18.922s = 0x0002e326 ticks (Some(63400) ticks since last time at 10000 Hz)
[4] Waited roughly 2.000s. Now is 24.875s = 0x0003cbb7 ticks (Some(59537) ticks since last time at 10000 Hz)
[5] Waited roughly 2.500s. Now is 31.215s = 0x0004c35f ticks (Some(63400) ticks since last time at 10000 Hz)
...

There are roughly 6 seconds between loop iterations, whereas the waiting time should be of 500ms. Or is that a side-effect of Tockilator's tracing, which slows down everything?

I don't see any 10KHz frequency in https://github.com/tock/tock/blob/master/kernel/src/hil/time.rs.

@bradjc
Copy link
Contributor

bradjc commented Apr 6, 2020

I believe the "bug" is in the lowrisc uart driver, specifically this block:

if self.tx_index.get() == self.tx_len.get() {
self.tx_client.map(|client| {
self.tx_buffer.take().map(|tx_buf| {
client.transmitted_buffer(tx_buf, self.tx_len.get(), ReturnCode::SUCCESS);
});
});
}
}

That client callback is in the transmit_buffer code path, but callbacks should only be issued in response to interrupts. In virtual_uart, the line self.inflight.set(node); can expect to run before the callback occurs. That is why I wrote "bug", since this is a product of Tock's particular concurrency model.

We also have the DeferredCall mechanism, which virtual_uart uses to make sure that a callback only happens after an "interrupt"(simulated with a deferred call), even when an error occurs.

@bcantrill
Copy link
Contributor Author

Btw, the frequency seem off on your platform.

OpenTitan initialisation complete. Entering main loop
[0] Waited roughly 0.000s. Now is 0.304s = 0x00000be5 ticks (None ticks since last time at 10000 Hz)
[1] Waited roughly 0.500s. Now is 6.438s = 0x0000fb7c ticks (Some(61335) ticks since last time at 10000 Hz)
[2] Waited roughly 1.000s. Now is 12.582s = 0x0001eb7e ticks (Some(61442) ticks since last time at 10000 Hz)
[3] Waited roughly 1.500s. Now is 18.922s = 0x0002e326 ticks (Some(63400) ticks since last time at 10000 Hz)
[4] Waited roughly 2.000s. Now is 24.875s = 0x0003cbb7 ticks (Some(59537) ticks since last time at 10000 Hz)
[5] Waited roughly 2.500s. Now is 31.215s = 0x0004c35f ticks (Some(63400) ticks since last time at 10000 Hz)
...

There are roughly 6 seconds between loop iterations, whereas the waiting time should be of 500ms. Or is that a side-effect of Tockilator's tracing, which slows down everything?

I don't see any 10KHz frequency in https://github.com/tock/tock/blob/master/kernel/src/hil/time.rs.

It's a side effect of running Ibex in Verilator, which (as you can imagine!) does indeed distort time. To run under Verialtor, you need to set CHIP_FREQ to 500_000 and UART_BAUD to 9_600. @pfmooney has a patch to do this under a config option (which is what we use to compile), but a separate issue should be opened on this...

@bcantrill
Copy link
Contributor Author

I believe the "bug" is in the lowrisc uart driver, specifically this block:

if self.tx_index.get() == self.tx_len.get() {
self.tx_client.map(|client| {
self.tx_buffer.take().map(|tx_buf| {
client.transmitted_buffer(tx_buf, self.tx_len.get(), ReturnCode::SUCCESS);
});
});
}
}

That client callback is in the transmit_buffer code path, but callbacks should only be issued in response to interrupts. In virtual_uart, the line self.inflight.set(node); can expect to run before the callback occurs. That is why I wrote "bug", since this is a product of Tock's particular concurrency model.

We also have the DeferredCall mechanism, which virtual_uart uses to make sure that a callback only happens after an "interrupt"(simulated with a deferred call), even when an error occurs.

Well, considering that one of the most basic libtock-rs examples doesn't currently run on OpenTitan/Earl Grey, I would say that it's just a bug -- air quotes definitely not required!

I will leave to others whether the bug is in the UART driver or in the virtual UART, but if the bug is believed to be in the UART vs. the virtual UART, I would add that:

  1. It seems like the use of self.inflight may still be incorrect as written as it will be set even if nothing is sent to the underlying device.

  2. The contract between the virtual UART and the UART is clearly more intricate than documented -- and in particular, the implicitly depended upon context for transmitted_buffer should be spelled out much more clearly. Here is that comment currently, which makes no mention of transmitted_buffer only be called asynchronously with respect to a call to transmit_buffer:

tock/kernel/src/hil/uart.rs

Lines 224 to 242 in b02fe52

/// A call to `Transmit::transmit_buffer` completed. The `ReturnCode`
/// indicates whether the buffer was successfully transmitted. A call
/// to `transmit_word` or `transmit_buffer` made within this callback
/// SHOULD NOT return EBUSY: when this callback is made the UART should
/// be ready to receive another call.
///
/// The `tx_len` argument specifies how many words were transmitted.
/// An `rval` of SUCCESS indicates that every requested word was
/// transmitted: `tx_len` in the callback should be the same as
/// `tx_len` in the initiating call.
///
/// `rval` is SUCCESS if the full buffer was successfully transmitted, or
/// - ECANCEL if the call to `transmit_buffer` was cancelled and
/// the buffer was not fully transmitted. `tx_len` contains
/// how many words were transmitted.
/// - ESIZE if the buffer could only be partially transmitted. `tx_len`
/// contains how many words were transmitted.
/// - FAIL if the transmission failed in some way.
fn transmitted_buffer(&self, tx_buffer: &'static mut [u8], tx_len: usize, rval: ReturnCode);

  1. This model seems incredibly brittle, as per @gendx's observation that there are (or have been) quite a few issues in this area -- and the system does little to prevent them.

@alevy
Copy link
Member

alevy commented Apr 6, 2020

To summarize:

  • Tockilator is still a very cool tool
  • There is bug (no air-quotes) in the libtock-rs + OpenTitan/EarlyGrey stack
  • The bug currently stems from the low-level UART driver "incorrectly" (air-quotes required) issuing a callback in the same call stack as the down-call to transmit.
  • These kinds of bugs have shown up in the past, particularly when developers who have yet to be burned by these kinds of bugs contribute otherwise wonderful code. It's currently: not well explained anywhere, not well tested, and (empirically at least) quite hard to catch during code-review.

Suggestions:

  • Continue using Tockilator
  • Acknowledge there is a bug
  • The bug seems fixable: don't call tx_progress from the transmit_buffer and instead only begin the transmission---rely on interrupts, even in the single-byte case, to relay callbacks to the client.
    • Document this requirement better
    • Figure out how to test for these behaviors
    • Perhaps come up with abstractions that make it harder to make these "mistakes"

@lschuermann
Copy link
Member

lschuermann commented Apr 6, 2020

These kinds of bugs have shown up in the past, particularly when developers who have yet to be burned by these kinds of bugs contribute otherwise wonderful code

Speaking from experience here, been tearing my hair out on one of these bugs for weeks: Many developers will run into these issues way before even trying to contribute. Requiring a callback from a different call stack (using deferred calls or interrupts) is something very common within Tock, which is as @alevy beautifully outlined not something obvious and even worse, not particularly good or visibly documented. This is a core concept every Tock kernel developer should know about and requires careful and beginner friendly explanation somewhere near the beginning of a getting started guide. I don't know the best place to put it but given a pointer can certainly try to draft a documentation.

@alevy
Copy link
Member

alevy commented Apr 6, 2020

@lschuermann a draft document is the best (and only) place to start for any document! And I agree it really needs to be very visible.

@alevy
Copy link
Member

alevy commented Apr 6, 2020

For better abstractions, @bradjc had an interesting idea: use a capability for doing "upcalls" that would be required by traits that define upcalls and would be passed up somewhere very low in the interrupt handling chain.

To flesh this out a tiny bit, you might imagine something like:

unsafe trait UpCallCap {}

trait TransmitClient {
    /// Just the existing client trait except it takes an `upcall` capability
    fn transmitted_buffer(&self, upcall: &mut UpCallCap, tx_buffer: &'static mut [u8], tx_len: usize, rval: ReturnCode);

   ...
}

trait Transmit {
    /// This is unchanged
    fn transmit_buffer(&self,
        tx_buffer: &'static mut [u8],
        tx_len: usize,
    ) -> (ReturnCode, Option<&'static mut [u8]>);

   ...
}

impl OpenTitanUart {
    pub fn handle_interrupt(&self, upcall: &mut UpCallCap) {
        // do stuff
        self.client.map(|client| client.transmitted_buffer(upcall, buffer, ...));
        // etc
    }
}

impl Transmit for OpenTitanUart {
    // Note that this doesn't have access to an `UpCallCap` capability
    fn transmit_buffer(&self,
        tx_buffer: &'static mut [u8],
        tx_len: usize,
    ) -> (ReturnCode, Option<&'static mut [u8]>) {
       if tx_len == 1 {
           self.registers.tx.write(tx_buffer[0]);
           // Oh how I wish I could just call `transmitted_buffer` on the client, but I can't because I don't have the capability to do so, so I guess I'll rewrite my logic to work through the interrupt call path!
       }
    }
   ...
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants