<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>Emulating the Badge Under Renode</title> <link rel="stylesheet" href="dist/reset.css"> <link rel="stylesheet" href="dist/reveal.css"> <link rel="stylesheet" href="dist/theme/fossasia2023.css"> <!-- Theme used for syntax highlighted code --> <link rel="stylesheet" href="plugin/highlight/zenburn.css"> </head> <body> <div class="reveal"> <div class="footer"> <a class="url" href="https://p.xobs.io/c23/">p.xobs.io/fa23</a> </div> <div class="slides"> <section> <h2>Emulating the Badge Under Renode</h2> <h3>(Eventually)</h3> <br /> <h3>Sean "xobs" Cross</h3> </section> <section> <h2>About Me</h2> <img data-src="img/xobs-projects.jpg"> </section> <section> <h2>Talk Outline</h2> <ol> <li>What is Renode?</li> <li>How is it extensible?</li> <li>What is the state of the badge?</li> </ol> </section> <section> <section> <h2>What is Renode?</h2> </section> <section> <h2>Whole-system Emulator</h2> <ul> <li>CPU cores</li> <li>Peripherals</li> <li>Interconnections</li> </ul> </section> <section> <h2>Target platforms</h2> <ul> <li>Windows</li> <li>Mac</li> <li>Linux</li> <li> <ul> <li>CI</li> </ul> </li> </ul> </section> <section> <h2>CPU Cores</h2> <ul> <li>x86</li> <li>arm</li> <li>arm64</li> <li>ppc</li> <li>riscv</li> <li>spark</li> <li>xtensa</li> </ul> </section> <section> <h2>Peripherals</h2> <img data-src="img/esp32s3-peripheral-list.png"> </section> <section> <h2>Peripherals Are Just Special Memory</h2> <ul> <li>Writing to a memory address does a thing</li> <li>Reading from an address gets a result</li> <li>Interrupts are just GPIOs</li> </ul> </section> <section> <h2>10% of the Functionality Gets You 90% of the Way There</h2> <ul> <li>E.g. in a serial device, baudrate, timers, and parity/stop can be ignored</li> <li>Just "Tx", "Rx", and interrupt setup</li> <li>Most software works just fine if you return zeroes for invalid addresses</li> </ul> </section> <section> <h2>SVD files are your friend</h2> <ul> <li>Your chip probably comes with a SVD file</li> <li>This gives a map of RAM and peripherals</li> <li>Renode can parse this and use it for logging / defaults</li> </ul> </section> <section> <h2>Existing Peripherals in Renode</h2> <ul> <li>Built-in peripherals for a number of devices</li> <ul> <li>STM32</li> <li>NRF52</li> <li>IMXRT</li> <li>LiteX</li> </ul> <li>See https://github.com/renode/renode-infrastructure/tree/master/src/Emulator/Peripherals/Peripherals for more</li> </ul> </section> <section> <h2>Creating Peripherals</h2> <ul> <li>Renode is written in C#</li> <li>Like Java, C# has an `eval()` function</li> <li>Peripherals can be written and loaded at runtime</li> </ul> </section> <section> <h2>Networks in Renode</h2> <ul> <li>Built-in support for CAN, Ethernet, and Wireless</li> <li>Also possible to add networks at runtime</li> <li>No latency, just packets of data</li> <ul> <li>Different devices run at different speeds</li> </ul> </ul> </section> <section> <h2>Other Goodies</h2> <ul> <li>LCD monitor with touchscreen support</li> <li>Python interpreter for quick hacks</li> <li>Attach GDB to any CPU core</li> <li>Attach a serial port to a network connection</li> <li>Scriptable with Robot framework</li> <li>Log function calls</li> </ul> </section> <section> <h2>Shortcomings</h2> <ul> <li>Unable to unload modules</li> <ul> <li>Need to restart Renode when you change a file</li> </ul> <li>No real audio support</li> <ul> <li>Support for verifying I2S, but can't play it</li> </ul> <li>Only x64 hosts</li> <ul> <li>Runs under Rosetta on Mac</li> </ul> <li>Documentation needs work</li> <ul> <li>Use the source!</li> </ul> </ul> </section> </section> <section> <section> <h2>Extending Renode</h2> </section> <section> <h2>First, Betrusted</h2> <ul> <li>FPGA with VexRiscv</li> <li>2nd FPGA with smaller VexRiscv</li> <li>16-bit COM bus between them</li> <li>AES extensions</li> <li>x25519 "ENGINE" accelerator</li> <li>SHA accelerator</li> <li>Battery charger and manager</li> <li>Custom lcd, timers, and USB</li> </ul> </section> <section> <h2>Emulating it under Renode</h2> <ul> <li>Good enough to develop the OS!</li> <li>Good enough to catch bugs</li> </ul> </section> <section> <h2>ENGINE bug</h2> <ol> <li>Added test vectors to the OS</li> <li>Ran test vectors on physical device</li> <li>Got test vectors passing in Renode</li> <li> <ul> <li>Yay!</li> </ul> </li> <li>Someone (without a device!) decided to add more vectors</li> <li>Tested in Renode</li> <li>Passed</li> <li>Failed on hardware</li> <li>Bug in detecting overflow condition for normalization in hardware</li> <li>https://github.com/betrusted-io/gateware/commit/817e284a3d92037b8cb0686735578d2bb60853e9 </li> </ol> </section> <section> <h2>Getting Started with Renode</h2> <ol> <li>Renode Platform Definition (<code>.repl</code>)</li> <li>Renode Script (<code>.resc</code>)</li> </ol> </section> <section> <h2>Example project repl</h2> </section> <section> <h2>Example project resc</h2> </section> <section> <h2>Running it</h2> </section> <section> <h2>How to Extend Renode</h2> <ol> <li>Find a peripheral that does what you want</li> <li> <ul> <li>You might even find a compatible peripheral!</li> </ul> </li> <li>Copy it to your project</li> <li>Change the constructor</li> <li>Change the register set</li> <li>Import the `.cs` file into Renode</li> <li>Add it to your platform file</li> </ol> </section> </section> <section> <section> <h1>Demonstration!</h1> </section> </section> <section> <section> <h2>Running Badge Software in Renode</h2> </section> <section> <h2>It Doesn't</h2> </section> <section> <h2>Xtensa is an Uncommon Architecture</h2> <ul> <li>Audio DSPs</li> <li>Possibly Intel?</li> <li>NXP</li> </ul> </section> <section> <h2>Extensive use of Boot ROM</h2> <ul> <img data-src="img/memory-maps.png"> </ul> </section> <section> <h2>Extensive use of Boot ROM</h2> <ul> <pre><code class="wrap">$ esptool.py dump_mem \ 0x3ff90000 \ 65536 \ irom.bin</code></pre> </ul> </section> <!-- <section> <ul> <li>Immediately get output</li> <li>Breaks immediately</li> </ul> </section> --> <section> <h2>Add a serial port</h2> <code><pre> namespace Antmicro.Renode.Peripherals.UART { public class ESP32_UART : UARTBase, IDoubleWordPeripheral, IKnownSize { private readonly DoubleWordRegisterCollection registers; public ESP32_UART(Machine machine) : base(machine) { registers = new DoubleWordRegisterCollection(this, new Dictionary<long, DoubleWordRegister> { {0x00, new DoubleWordRegister(this).WithValueField(0,8, writeCallback: (_, v) => TransmitCharacter((byte)v))} }); }</code></code> </section> <section> <h2>Add a serial port</h2> <code><pre> {0x00, new DoubleWordRegister(this) .WithValueField(0, 8, writeCallback: (_, v) => TransmitCharacter((byte)v))}</code></code> </section> <section> <img data-src="img/esp32s3-uart.png"> </section> <section> <h2>Load SVD file</h2> <pre><code>sysbus: init: ApplySVD @esp32s3.svd</code></pre> </section> <section> <h2>Load SVD file</h2> <pre><code style="word-wrap: break-word;"> extmem: Unhandled read from offset 0x64. extmem: Unhandled write to offset 0x64, value 0x3. sysbus: Read from an unimplemented register SYSTEM:SYSCLK_CONF (0x600C0060), returning a value from SVD: 0x1. (3) sysbus: Read from an unimplemented register SYSTEM:PERIP_CLK_EN0 (0x600C0018), returning a value from SVD: 0xF9C1E06F. sysbus: Write of value 0xF9C1E06F to an unimplemented register SYSTEM:PERIP_CLK_EN0 (0x600C0018) generated from SVD. sysbus: Read from an unimplemented register SYSTEM:PERIP_RST_EN0 (0x600C0020), returning a value from SVD: 0x0. sysbus: Write of value 0x0 to an unimplemented register SYSTEM:PERIP_RST_EN0 0x600C0020) generated from SVD. sysbus: Read from an unimplemented register SYSTEM:PERIP_RST_EN0 (0x600C0020), returning a value from SVD: 0x0. sysbus: Write of value 0x4 to an unimplemented register SYSTEM:PERIP_RST_EN0 (0x600C0020) generated from SVD. </code></pre> </section> <section> <h2>Add Peripherals</h2> </section> <section> <h2>Slowly Advance</h2> <img data-src="img/esp32s3-more-boot.png"> </section> <section> <h2>GDB with ELF</h2> <img data-src="img/gdb-with-elf.png"> </section> <section> <h2>What's Left?</h2> <ul> <li>SPI Controller</li> <li>Interrupt Controller</li> </ul> </section> </section> <section> <h2>Getting involved</h2> <ul> <li>Try Renode for your projects!</li> <li>Add CI tests for your firmware!</li> <li>Explore binaries for unknown targets!</li> </ul> <h2 class="fragment">Thank you</h2> <h3 class="fragment">Questions?</h3> </section> </div> </div> <script src="dist/reveal.js"></script> <script src="plugin/notes/notes.js"></script> <script src="plugin/zoom/zoom.js"></script> <script src="plugin/markdown/markdown.js"></script> <script src="plugin/highlight/highlight.js"></script> <script> /** This used to be a part of Reveal.js, but was removed at some point */ function getQueryHash() { function deserialize(value) { if (typeof value === 'string') { if (value === 'null') return null; else if (value === 'true') return true; else if (value === 'false') return false; else if (value.match(/^-?[\d\.]+$/)) return parseFloat(value); } return value; } let query = {}; location.search.replace(/[A-Z0-9]+?=([\w\.%-]*)/gi, a => { query[a.split('=').shift()] = a.split('=').pop(); }); // Basic deserialization for (let i in query) { let value = query[i]; query[i] = deserialize(unescape(value)); } // Do not accept new dependencies via query config to avoid // the potential of malicious script injection if (typeof query['dependencies'] !== 'undefined') delete query['dependencies']; return query; } var presenter = !!getQueryHash().s; var stream = !!getQueryHash().stream; // More info about initialization & config: // - https://revealjs.com/initialization/ // - https://revealjs.com/config/ reveal_dependencies = [ { src: 'https://reveal-multiplex.glitch.me/socket.io/socket.io.js', async: true }, ]; if (presenter) { reveal_dependencies.push({ src: 'https://reveal-multiplex.glitch.me/master.js', async: true },); } else { reveal_dependencies.push({ src: 'https://reveal-multiplex.glitch.me/client.js', async: true }); } Reveal.initialize({ hash: true, controls: presenter ? false : (stream ? false : true), progress: true, history: true, center: true, controlsTutorial: presenter ? false : (stream ? false : true), slideNumber: presenter ? null : (stream ? null : 'c/t'), // The "normal" size of the presentation, aspect ratio will be preserved // when the presentation is scaled to fit different resolutions. Can be // specified using percentage units. width: 1280, height: 840, // Factor of the display size that should remain empty around the content margin: 0.1, multiplex: { // Example values. To generate your own, see the socket.io server instructions. secret: getQueryHash().s || null, id: '863f0948d18284f3', url: 'https://p.xobs.io/' }, // Bounds for smallest/largest possible scale to apply to content minScale: 0.02, maxScale: 5.5, defaultTiming: 41, transition: 'slide', // none/fade/slide/convex/concave/zoom // Don't forget to add the dependencies dependencies: reveal_dependencies, // Learn about plugins: https://revealjs.com/plugins/ plugins: [RevealMarkdown, RevealHighlight, RevealZoom, RevealNotes] }); </script> </body> </html>