lupyuen.org/articles/semihost.html

1098 lines
No EOL
62 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="rustdoc">
<title>Star64 JH7110 + NuttX RTOS: RISC-V Semihosting and Initial RAM Disk</title>
<!-- Begin scripts/articles/*-header.html: Article Header for Custom Markdown files processed by rustdoc, like chip8.md -->
<meta property="og:title"
content="Star64 JH7110 + NuttX RTOS: RISC-V Semihosting and Initial RAM Disk"
data-rh="true">
<meta property="og:description"
content="Apache NuttX RTOS crashes on Pine64's Star64 JH7110 RISC-V SBC because there's no Semihosting. But no worries! We modified NuttX to boot with an Initial RAM Disk instead (initrd)."
data-rh="true">
<meta name="description"
content="Apache NuttX RTOS crashes on Pine64's Star64 JH7110 RISC-V SBC because there's no Semihosting. But no worries! We modified NuttX to boot with an Initial RAM Disk instead (initrd).">
<meta property="og:image"
content="https://lupyuen.github.io/images/semihost-title.jpg">
<meta property="og:type"
content="article" data-rh="true">
<link rel="canonical" href="https://lupyuen.org/articles/semihost.html" />
<!-- End scripts/articles/*-header.html -->
<!-- Begin scripts/rustdoc-header.html: Header for Custom Markdown files processed by rustdoc, like chip8.md -->
<link rel="alternate" type="application/rss+xml" title="RSS Feed for lupyuen" href="/rss.xml" />
<link rel="stylesheet" type="text/css" href="../normalize.css">
<link rel="stylesheet" type="text/css" href="../rustdoc.css" id="mainThemeStyle">
<link rel="stylesheet" type="text/css" href="../dark.css">
<link rel="stylesheet" type="text/css" href="../light.css" id="themeStyle">
<link rel="stylesheet" type="text/css" href="../prism.css">
<script src="../storage.js"></script><noscript>
<link rel="stylesheet" href="../noscript.css"></noscript>
<link rel="shortcut icon" href="../favicon.ico">
<style type="text/css">
#crate-search {
background-image: url("../down-arrow.svg");
}
</style>
<!-- End scripts/rustdoc-header.html -->
</head>
<body class="rustdoc">
<!--[if lte IE 8]>
<div class="warning">
This old browser is unsupported and will most likely display funky
things.
</div>
<![endif]-->
<!-- Begin scripts/rustdoc-before.html: Pre-HTML for Custom Markdown files processed by rustdoc, like chip8.md -->
<!-- Begin Theme Picker -->
<div class="theme-picker" style="left: 0"><button id="theme-picker" aria-label="Pick another theme!"><img src="../brush.svg"
width="18" alt="Pick another theme!"></button>
<div id="theme-choices"></div>
</div>
<!-- Theme Picker -->
<!-- End scripts/rustdoc-before.html -->
<h1 class="title">Star64 JH7110 + NuttX RTOS: RISC-V Semihosting and Initial RAM Disk</h1>
<nav id="rustdoc"><ul>
<li><a href="#nuttx-crashes-on-star64" title="NuttX Crashes On Star64">1 NuttX Crashes On Star64</a><ul></ul></li>
<li><a href="#decipher-the-risc-v-exception" title="Decipher the RISC-V Exception">2 Decipher the RISC-V Exception</a><ul></ul></li>
<li><a href="#nuttx-calls-semihosting" title="NuttX Calls Semihosting">3 NuttX Calls Semihosting</a><ul></ul></li>
<li><a href="#nuttx-apps-filesystem" title="NuttX Apps Filesystem">4 NuttX Apps Filesystem</a><ul></ul></li>
<li><a href="#semihosting-on-nuttx-qemu" title="Semihosting on NuttX QEMU">5 Semihosting on NuttX QEMU</a><ul></ul></li>
<li><a href="#modify-nuttx-qemu-for-initial-ram-disk" title="Modify NuttX QEMU for Initial RAM Disk">6 Modify NuttX QEMU for Initial RAM Disk</a><ul></ul></li>
<li><a href="#boot-nuttx-qemu-with-initial-ram-disk" title="Boot NuttX QEMU with Initial RAM Disk">7 Boot NuttX QEMU with Initial RAM Disk</a><ul></ul></li>
<li><a href="#nuttx-star64-with-initial-ram-disk" title="NuttX Star64 with Initial RAM Disk">8 NuttX Star64 with Initial RAM Disk</a><ul></ul></li>
<li><a href="#whats-next" title="Whats Next">9 Whats Next</a><ul></ul></li>
<li><a href="#appendix-boot-nuttx-over-tftp-with-initial-ram-disk" title="Appendix: Boot NuttX over TFTP with Initial RAM Disk">10 Appendix: Boot NuttX over TFTP with Initial RAM Disk</a><ul></ul></li>
<li><a href="#appendix-configure-nuttx-for-initial-ram-disk" title="Appendix: Configure NuttX for Initial RAM Disk">11 Appendix: Configure NuttX for Initial RAM Disk</a><ul></ul></li>
<li><a href="#appendix-ram-disk-address-for-risc-v-qemu" title="Appendix: RAM Disk Address for RISC-V QEMU">12 Appendix: RAM Disk Address for RISC-V QEMU</a><ul></ul></li>
<li><a href="#appendix-device-tree-for-risc-v-qemu" title="Appendix: Device Tree for RISC-V QEMU">13 Appendix: Device Tree for RISC-V QEMU</a><ul></ul></li>
<li><a href="#appendix-initial-ram-disk-for-litex-arty-a7" title="Appendix: Initial RAM Disk for LiteX Arty-A7">14 Appendix: Initial RAM Disk for LiteX Arty-A7</a><ul></ul></li></ul></nav><p>📝 <em>28 Jul 2023</em></p>
<p><img src="https://lupyuen.github.io/images/semihost-title.jpg" alt="Booting NuttX on Star64 with Initial RAM Disk" /></p>
<blockquote>
<p><em>Once upon a time: There was a Very Naive Bloke (me!) who connected a <strong>Smartwatch to the internet…</strong></em></p>
</blockquote>
<blockquote>
<p><em>Anyone in world could <strong>flash their own firmware</strong> on the watch, and watch it run on a <strong>Live Video Stream</strong>!</em></p>
</blockquote>
<blockquote>
<p><em>Until a Wise Person (politely) flashed some <strong>very clever firmware</strong> on the watch, that could <strong>access other devices</strong> connected to the watch…</em></p>
</blockquote>
<blockquote>
<p><em>All because of <strong>Semihosting</strong>!</em></p>
</blockquote>
<p>Yep <a href="https://liliputing.com/you-can-flash-firmware-on-this-pinetime-smartwatch-in-singapore-over-the-internet/"><strong>this really happened!</strong></a> (Thankfully it was a <a href="https://github.com/lupyuen/remote-pinetime-bot/blob/master/README.md#semihosting-security"><strong>harmless experiment</strong></a>)</p>
<p>Three years later were still having <strong>Semihosting Problems</strong>, but on a different gadget: the <a href="https://wiki.pine64.org/wiki/STAR64"><strong>Pine64 Star64</strong></a> 64-bit RISC-V Single-Board Computer. (Pic below)</p>
<p>(Based on <a href="https://doc-en.rvspace.org/Doc_Center/jh7110.html"><strong>StarFive JH7110</strong></a>, the same SoC in VisionFive2)</p>
<p>In this article, we find out…</p>
<ul>
<li>
<p>Whats <strong>RISC-V Semihosting</strong></p>
</li>
<li>
<p>Why it crashes <a href="https://lupyuen.github.io/articles/nuttx2"><strong>Apache NuttX RTOS</strong></a> on Star64</p>
</li>
<li>
<p>How it affects the <strong>Apps Filesystem</strong> in NuttX</p>
</li>
<li>
<p>How we replaced Semihosting by <strong>Initial RAM Disk “initrd”</strong> (pic above)</p>
</li>
<li>
<p>After testing on <strong>QEMU Emulator</strong></p>
</li>
<li>
<p>Thanks to NuttX on <strong>LiteX Arty-A7</strong> for the guidance!</p>
</li>
</ul>
<p><img src="https://lupyuen.github.io/images/nuttx2-star64.jpg" alt="Star64 RISC-V SBC" /></p>
<h1 id="nuttx-crashes-on-star64"><a class="doc-anchor" href="#nuttx-crashes-on-star64">§</a>1 NuttX Crashes On Star64</h1>
<p>In the last article, we tried porting Apache NuttX RTOS from <strong>QEMU Emulator to Star64 JH7110 SBC</strong></p>
<ul>
<li><a href="https://lupyuen.github.io/articles/privilege"><strong>“Star64 JH7110 + NuttX RTOS: RISC-V Privilege Levels and UART Registers”</strong></a></li>
</ul>
<p>NuttX seems to boot OK for a while…</p>
<div class="example-wrap"><pre class="language-text"><code>123067DFHBC
qemu_rv_kernel_mappings: map I/O regions
qemu_rv_kernel_mappings: map kernel text
qemu_rv_kernel_mappings: map kernel data
qemu_rv_kernel_mappings: connect the L1 and L2 page tables
qemu_rv_kernel_mappings: map the page pool
qemu_rv_mm_init: mmu_enable: satp=1077956608
Inx_start: Entry
elf_initialize: Registering ELF
uart_register: Registering /dev/console
uart_register: Registering /dev/ttyS0
work_start_lowpri: Starting low-priority kernel worker thread(s)
nx_start_application: Starting init task: /system/bin/init
load_absmodule: Loading /system/bin/init
elf_loadbinary: Loading file: /system/bin/init
elf_init: filename: /system/bin/init loadinfo: 0x404069e8</code></pre></div>
<p><a href="https://github.com/lupyuen/nuttx-star64/blob/6f422cb3075f57e2acf312edcc21112fe42660e8/README.md#initialise-risc-v-supervisor-mode">(Source)</a></p>
<p>But then NuttX crashes with a <strong>RISC-V Exception</strong></p>
<div class="example-wrap"><pre class="language-text"><code>EXCEPTION: Breakpoint
MCAUSE: 00000003
EPC: 40200434
MTVAL: 00000000</code></pre></div>
<p><a href="https://github.com/lupyuen/nuttx-star64/blob/6f422cb3075f57e2acf312edcc21112fe42660e8/README.md#initialise-risc-v-supervisor-mode">(Source)</a></p>
<p>Lets find out why…</p>
<p><img src="https://lupyuen.github.io/images/privilege-run2.png" alt="NuttX crashes due to a Semihosting Problem" /></p>
<h1 id="decipher-the-risc-v-exception"><a class="doc-anchor" href="#decipher-the-risc-v-exception">§</a>2 Decipher the RISC-V Exception</h1>
<p><em>NuttX crashes with this RISC-V Exception…</em></p>
<p><em>What does it mean?</em></p>
<div class="example-wrap"><pre class="language-text"><code>EXCEPTION: Breakpoint
MCAUSE: 00000003
EPC: 40200434
MTVAL: 00000000</code></pre></div>
<p><a href="https://github.com/lupyuen/nuttx-star64/blob/6f422cb3075f57e2acf312edcc21112fe42660e8/README.md#initialise-risc-v-supervisor-mode">(Source)</a></p>
<p>According to the <a href="https://five-embeddev.com/riscv-isa-manual/latest/machine.html#sec:mcause"><strong>Machine Cause Register (MCAUSE)</strong></a>, value 3 says that its a <strong>“Machine Software Interrupt”</strong>.</p>
<p>Which means that NuttX has intentionally triggered a <strong>Software Interrupt</strong>. Probably to execute a Special Function.</p>
<p><em>Something special? Like what?</em></p>
<p>We look up the <strong>Exception Program Counter (EPC) <code>0x4020</code> <code>0434</code></strong> in our NuttX Disassembly…</p>
<div class="example-wrap"><pre class="language-text"><code>nuttx/arch/risc-v/src/common/riscv_semihost.S:37
smh_call():
// Register A0 contains the Semihosting Operation Number.
// Register A1 contains the Semihosting Parameter.
// Shift Left (does nothing)
40200430: 01f01013 slli zero, zero, 0x1f
// Crashes here:
// Trigger Semihosting Breakpoint
40200434: 00100073 ebreak
// Shift Right (does nothing)
// Encodes the Semihosting Call Number 7
40200438: 40705013 srai zero, zero, 0x7</code></pre></div>
<p><a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_semihost.S#L38">(Source)</a></p>
<p>The code above has a special RISC-V Instruction…</p>
<div class="example-wrap"><pre class="language-text"><code>ebreak</code></pre></div>
<p><em>Whats this ebreak?</em></p>
<p>From the <a href="https://five-embeddev.com/quickref/instructions.html#-rv32--environment-call-and-breakpoints"><strong>RISC-V Spec</strong></a></p>
<blockquote>
<p>“The EBREAK instruction is used to return control to a debugging environment”</p>
</blockquote>
<blockquote>
<p>“EBREAK was primarily designed to be used by a debugger to cause execution to stop and fall back into the debugger”</p>
</blockquote>
<p>OK thanks but were not doing any debugging!</p>
<p>The next part is more helpful…</p>
<blockquote>
<p>“Another use of EBREAK is to support <strong>Semihosting</strong>, where the execution environment includes a debugger that can provide services over an Alternate System Call Interface built around the EBREAK instruction”</p>
</blockquote>
<p>Aha! NuttX is making a special <a href="https://embeddedinn.xyz/articles/tutorial/understanding-riscv-semihosting/"><strong>System Call to Semihosting</strong></a>!</p>
<p>(Well see why)</p>
<blockquote>
<p>“Because the RISC-V base ISA does not provide more than one EBREAK instruction, RISC-V Semihosting uses a <strong>special sequence of instructions</strong> to distinguish a Semihosting EBREAK from a Debugger Inserted EBREAK”</p>
</blockquote>
<p>Which explains this (strange) preceding RISC-V Instruction…</p>
<div class="example-wrap"><pre class="language-text"><code>// Shift Left the value 0x1F
// into Register X0...
// Which is always 0!
slli zero, zero, 0x1f</code></pre></div>
<p>That doesnt do anything meaningful!</p>
<p>Lets talk about Semihosting…</p>
<p><img src="https://lupyuen.github.io/images/semihost-qemu3.jpg" alt="NuttX calls Semihosting to read the Apps Filesystem" /></p>
<h1 id="nuttx-calls-semihosting"><a class="doc-anchor" href="#nuttx-calls-semihosting">§</a>3 NuttX Calls Semihosting</h1>
<p><em>Who calls ebreak? And why?</em></p>
<p><strong><code>ebreak</code></strong> is called by <a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_semihost.S#L20-L40"><strong>smh_call</strong></a>, which is called by <a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_hostfs.c#L52-L71"><strong>host_call</strong></a></p>
<div class="example-wrap"><pre class="language-c"><code>// NuttX calls Semihosting to
// access the Host Filesystem
static long host_call(
unsigned int nbr, // Semihosting Operation Number
void *parm, // Semihosting Parameter
size_t size // Size of Parameter
) {
// Call Semihosting via `ebreak`
long ret = smh_call(
nbr, // Semihosting Operation Number
parm // Semihosting Parameter
);</code></pre></div>
<p><a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_hostfs.c#L52-L71">(Source)</a></p>
<p><em>Whats this operation number?</em></p>
<p>The <strong>Semihosting Operation Numbers</strong> are defined here: <a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_hostfs.c#L38-L48">riscv_hostfs.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// Semihosting Operation Numbers
// (For File Operations)
#define HOST_OPEN 0x01
#define HOST_CLOSE 0x02
#define HOST_WRITE 0x05
#define HOST_READ 0x06
#define HOST_SEEK 0x0a
#define HOST_FLEN 0x0c
#define HOST_REMOVE 0x0e
#define HOST_RENAME 0x0f
#define HOST_ERROR 0x13</code></pre></div>
<p><em>Aha! NuttX is calling Semihosting to access the File System!</em></p>
<p>Indeed! When we log <a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_hostfs.c#L52-L71"><strong>host_call</strong></a>, we see…</p>
<div class="example-wrap"><pre class="language-text"><code>host_call:
nbr=0x1 (HOST_OPEN)
parm=0x40406778
size=24</code></pre></div>
<p>Which calls Semihosting to <strong>open a file.</strong></p>
<p><em>Open what file?</em></p>
<p>If we look back at the <strong>NuttX Crash Log</strong></p>
<div class="example-wrap"><pre class="language-text"><code>nx_start_application:
Starting init task: /system/bin/init
load_absmodule:
Loading /system/bin/init
elf_loadbinary:
Loading file: /system/bin/init
elf_init: filename:
/system/bin/init loadinfo: 0x404069e8
riscv_exception:
EXCEPTION: Breakpoint</code></pre></div>
<p><a href="https://github.com/lupyuen/nuttx-star64/blob/6f422cb3075f57e2acf312edcc21112fe42660e8/README.md#initialise-risc-v-supervisor-mode">(Source)</a></p>
<p>NuttX is trying to read the file <strong>/system/bin/init</strong> via Semihosting!</p>
<p>Why did it fail? Lets find out…</p>
<h1 id="nuttx-apps-filesystem"><a class="doc-anchor" href="#nuttx-apps-filesystem">§</a>4 NuttX Apps Filesystem</h1>
<p><em>Whats /system/bin/init?</em></p>
<p><em>Why is NuttX reading it at startup?</em></p>
<p>Remember we copied <strong>NuttX from QEMU</strong> and (naively) ran it on Star64?</p>
<p>We backtrack to the origin (NuttX on QEMU) and figure out whats <strong>/system/bin/init</strong></p>
<div class="example-wrap"><pre class="language-bash"><code>## Build NuttX QEMU in Kernel Mode
tools/configure.sh rv-virt:knsh64
make V=1 -j7
## Build Apps Filesystem for NuttX QEMU
make export V=1
pushd ../apps
./tools/mkimport.sh \
-z -x \
../nuttx/nuttx-export-*.tar.gz
make import V=1
popd
## Dump the `init` disassembly to `init.S`
riscv64-unknown-elf-objdump \
-t -S --demangle --line-numbers --wide \
../apps/bin/init \
&gt;init.S \
2&gt;&amp;1</code></pre></div>
<p><a href="https://nuttx.apache.org/docs/latest/platforms/risc-v/qemu-rv/boards/rv-virt/index.html">(Source)</a></p>
<p><a href="https://lupyuen.github.io/articles/privilege#nuttx-flat-mode-becomes-kernel-mode">(Why we use <strong>Kernel Mode</strong>)</a></p>
<p>The above commands will build the <strong>Apps Filesystem</strong> for NuttX QEMU.</p>
<p>Which includes <strong>/system/bin/init</strong></p>
<div class="example-wrap"><pre class="language-bash"><code>$ ls ../apps/bin
getprime
hello
init
sh</code></pre></div>
<p><em>Isnt it supposed to be /system/bin/init? Not /apps/bin/init?</em></p>
<p>When we check the <strong>NuttX Build Configuration</strong></p>
<div class="example-wrap"><pre class="language-bash"><code>$ grep INIT .config
CONFIG_INIT_FILE=y
CONFIG_INIT_ARGS=&quot;&quot;
CONFIG_INIT_FILEPATH=&quot;/system/bin/init&quot;
CONFIG_INIT_MOUNT=y
CONFIG_INIT_MOUNT_SOURCE=&quot;&quot;
CONFIG_INIT_MOUNT_TARGET=&quot;/system&quot;
CONFIG_INIT_MOUNT_FSTYPE=&quot;hostfs&quot;
CONFIG_INIT_MOUNT_FLAGS=0x1
CONFIG_INIT_MOUNT_DATA=&quot;fs=../apps&quot;
CONFIG_PATH_INITIAL=&quot;/system/bin&quot;
CONFIG_NSH_ARCHINIT=y</code></pre></div>
<p><a href="https://github.com/apache/nuttx/blob/master/boards/risc-v/qemu-rv/rv-virt/configs/knsh64/defconfig">(Source)</a></p>
<p>We see that NuttX will mount the <strong>/apps</strong> filesystem as <strong>/system</strong>, via the <a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/common/riscv_hostfs.c"><strong>Semihosting Host Filesystem</strong></a>.</p>
<p>Thats why it appears as <strong>/system/bin/init</strong>!</p>
<p><em>Whats inside /system/bin/init?</em></p>
<p>The RISC-V Disassembly of <strong>/system/bin/init</strong> shows this…</p>
<div class="example-wrap"><pre class="language-text"><code>apps/system/nsh/nsh_main.c:52
0000006e &lt;main&gt;:
int main(int argc, FAR char *argv[]) {</code></pre></div>
<p><a href="https://github.com/lupyuen2/wip-nuttx/releases/download/star64c-0.0.1/init.S">(Source)</a></p>
<p>Yep its the Compiled ELF Executable of the <a href="https://github.com/lupyuen2/wip-nuttx-apps/blob/star64c/system/nsh/nsh_main.c#L40-L85"><strong>NuttX Shell <code>nsh</code></strong></a>!</p>
<p>Now everything makes sense…</p>
<ol>
<li>
<p>At Startup: NuttX tries to load <strong>/system/bin/init</strong> to start the <a href="https://github.com/lupyuen2/wip-nuttx-apps/blob/star64c/system/nsh/nsh_main.c#L40-L85"><strong>NuttX Shell <code>nsh</code></strong></a></p>
</li>
<li>
<p>But it Fails: Because <strong>/system/bin/init</strong> doesnt exist in the Semihosting Filesystem on Star64!</p>
</li>
</ol>
<p>This is why Semihosting wont work on Star64…</p>
<p><img src="https://lupyuen.github.io/images/semihost-qemu.jpg" alt="QEMU reads the Apps Filesystem over Semihosting" /></p>
<h1 id="semihosting-on-nuttx-qemu"><a class="doc-anchor" href="#semihosting-on-nuttx-qemu">§</a>5 Semihosting on NuttX QEMU</h1>
<p><em>Why Semihosting wont work on Star64 SBC?</em></p>
<p><a href="https://embeddedinn.xyz/articles/tutorial/understanding-riscv-semihosting/"><strong>Semihosting</strong></a> was created for <a href="https://en.wikipedia.org/wiki/Debugger#Hardware_support_for_debugging"><strong>Hardware Debuggers</strong></a> and <a href="https://en.wikipedia.org/wiki/Hypervisor"><strong>Virtual Machine Hypervisors</strong></a>, like QEMU Emulator.</p>
<p>The pic above shows how it works: Semihosting enables a Virtual Machine (like NuttX) to <strong>“Break Out” of its Sandbox</strong> to access the Filesystem on the Host Machine / Our Computer.</p>
<p>(Remember our story at the top of the article? Be careful with Semihosting!)</p>
<p>Thats why we <strong>Enable Semihosting</strong> when we run NuttX on QEMU…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Start NuttX on QEMU
## with Semihosting Enabled
qemu-system-riscv64 \
-kernel nuttx \
-cpu rv64 \
-M virt,aclint=on \
-semihosting \
-bios none \
-nographic</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/riscv#qemu-emulator-for-risc-v">(Source)</a></p>
<p>(Remove <strong><code>-bios none</code></strong> for newer versions of NuttX)</p>
<p>So that NuttX can access the <strong>Apps Filesystem</strong> (from previous section) as a Semihosting Filesystem! (Pic above)</p>
<p><a href="https://embeddedinn.xyz/articles/tutorial/understanding-riscv-semihosting/">(More about <strong>RISC-V Semihosting</strong>)</a></p>
<p><a href="https://github.com/riscv-software-src/riscv-semihosting/blob/main/riscv-semihosting-spec.adoc">(See the <strong>Semihosting Spec</strong>)</a></p>
<p><em>This wont work on Star64?</em></p>
<p>Semihosting wont work because NuttX for Star64 runs on <strong>Real SBC Hardware</strong> (Bare Metal)…</p>
<p>Theres nothing to “break out” to!</p>
<p><img src="https://lupyuen.github.io/images/semihost-star64a.jpg" alt="Initial RAM Disk for NuttX" /></p>
<p><em>If not Semihosting… Then what?</em></p>
<p>In the world of Linux (and QEMU), theres something cool called an <a href="https://en.wikipedia.org/wiki/Initial_ramdisk"><strong>Initial RAM Disk (initrd)</strong></a></p>
<ul>
<li>
<p>Its a <strong>RAM Disk</strong>, located in RAM (pic above)</p>
</li>
<li>
<p>But its an <strong>Initial</strong> RAM Disk. Which means theres a Filesystem inside, preloaded with Files and Directories.</p>
</li>
</ul>
<p>Perfect for our NuttX Apps Filesystem!</p>
<p><em>Thats awesome but where do we start?</em></p>
<p>We begin by modding NuttX QEMU to load the Initial RAM Disk…</p>
<p><img src="https://lupyuen.github.io/images/semihost-qemu2.jpg" alt="NuttX for QEMU will mount the Apps Filesystem from an Initial RAM Disk" /></p>
<h1 id="modify-nuttx-qemu-for-initial-ram-disk"><a class="doc-anchor" href="#modify-nuttx-qemu-for-initial-ram-disk">§</a>6 Modify NuttX QEMU for Initial RAM Disk</h1>
<p><em>NuttX QEMU will load an Initial RAM Disk…</em></p>
<p><em>Instead of using Semihosting. How?</em></p>
<p>In the previous section, we said that…</p>
<ul>
<li>
<p><a href="https://en.wikipedia.org/wiki/Initial_ramdisk"><strong>Initial RAM Disk (initrd)</strong></a> is a <strong>RAM Disk</strong>, located in RAM (pic above)</p>
</li>
<li>
<p>But its an <strong>Initial</strong> RAM Disk. Which means theres a Filesystem inside, preloaded with Files and Directories.</p>
</li>
</ul>
<p>To modify NuttX QEMU to load an <strong>Initial RAM Disk</strong>, we define the address of the <strong>RAM Disk Memory</strong> in the Linker Script: <a href="https://github.com/lupyuen2/wip-nuttx/blob/ramdisk/boards/risc-v/qemu-rv/rv-virt/scripts/ld-kernel64.script#L20-L54">ld-kernel64.script</a></p>
<div class="example-wrap"><pre class="language-text"><code>MEMORY
{
...
/* Added RAM Disk Memory (Max 16 MB) */
ramdisk (rwx) : ORIGIN = 0x80800000, LENGTH = 16M /* w/ cache */
}
/* Increased Page Heap for RAM Disk */
__pgheap_size = LENGTH(pgram) + LENGTH(ramdisk);
/* Previously: __pgheap_size = LENGTH(pgram); */
/* Added RAM Disk Symbols */
__ramdisk_start = ORIGIN(ramdisk);
__ramdisk_size = LENGTH(ramdisk);
__ramdisk_end = ORIGIN(ramdisk) + LENGTH(ramdisk);</code></pre></div>
<p>(<strong><code>0x8080</code> <code>0000</code></strong> is the next available RAM Address)</p>
<p>At NuttX Startup, we <strong>mount the RAM Disk</strong>: <a href="https://github.com/lupyuen2/wip-nuttx/blob/ramdisk/boards/risc-v/qemu-rv/rv-virt/src/qemu_rv_appinit.c#L83-L179">qemu_rv_appinit.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// Called at NuttX Startup
void board_late_initialize(void) {
// Mount the RAM Disk
mount_ramdisk();
// Perform board-specific initialization
#ifdef CONFIG_NSH_ARCHINIT
mount(NULL, &quot;/proc&quot;, &quot;procfs&quot;, 0, NULL);
#endif
}
// Mount the RAM Disk
int mount_ramdisk(void) {
// Define the ROMFS
struct boardioc_romdisk_s desc;
desc.minor = RAMDISK_DEVICE_MINOR;
desc.nsectors = NSECTORS((ssize_t)__ramdisk_size);
desc.sectsize = SECTORSIZE;
desc.image = __ramdisk_start;
// Mount the ROMFS
int ret = boardctl(BOARDIOC_ROMDISK, (uintptr_t)&amp;desc);
// Omitted: Handle Errors</code></pre></div>
<p>(More about ROMFS in a while)</p>
<p>Before mounting, we copy the RAM Disk from <strong><code>0x8400</code> <code>0000</code></strong> to <strong>ramdisk_start</strong>: <a href="https://github.com/lupyuen2/wip-nuttx/blob/ramdisk/arch/risc-v/src/qemu-rv/qemu_rv_mm_init.c#L271-L280">qemu_rv_mm_init.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>void qemu_rv_kernel_mappings(void) {
...
// Copy RAM Disk from 0x8400 0000 to
// `__ramdisk_start` (`__ramdisk_size` bytes)
// TODO: RAM Disk must not exceed `__ramdisk_size` bytes
memcpy( // Copy the RAM Disk...
(void *)__ramdisk_start, // To RAM Disk Memory
(void *)0x84000000, // From QEMU initrd Address
(size_t)__ramdisk_size // For 16 MB
);</code></pre></div>
<p>(More about <strong><code>0x8400</code> <code>0000</code></strong> in a while)</p>
<p><a href="https://github.com/lupyuen2/wip-nuttx/blob/ramdisk/arch/risc-v/src/qemu-rv/qemu_rv_mm_init.c#L280-L287">(Somehow <strong>map_region</strong> crashes when we map the RAM Disk Memory)</a></p>
<p>Things get really wonky when we exceed the bounds of the RAM Disk. So we <strong>validate the bounds</strong>: <a href="https://github.com/lupyuen2/wip-nuttx/blob/ramdisk/fs/romfs/fs_romfsutil.c#L79-L84">fs_romfsutil.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// While reading from RAM Disk...
static uint32_t romfs_devread32(struct romfs_mountpt_s *rm, int ndx) {
// If we&#39;re reading beyond the bounds of
// RAM Disk Memory, halt (and catch fire)
DEBUGASSERT(
&amp;rm-&gt;rm_buffer[ndx] &lt;
__ramdisk_start + (size_t)__ramdisk_size
);</code></pre></div>
<p>Finally we configure NuttX QEMU to mount the <strong>Initial RAM Disk as ROMFS</strong> (instead of Semihosting): <a href="https://github.com/lupyuen2/wip-nuttx/blob/ramdisk/boards/risc-v/qemu-rv/rv-virt/configs/knsh64/defconfig">knsh64/defconfig</a></p>
<div class="example-wrap"><pre class="language-bash"><code>CONFIG_BOARDCTL_ROMDISK=y
CONFIG_BOARD_LATE_INITIALIZE=y
CONFIG_FS_ROMFS=y
CONFIG_INIT_FILEPATH=&quot;/system/bin/init&quot;
CONFIG_INIT_MOUNT=y
CONFIG_INIT_MOUNT_FLAGS=0x1
CONFIG_INIT_MOUNT_TARGET=&quot;/system/bin&quot;
## We removed these...
## CONFIG_FS_HOSTFS=y
## CONFIG_RISCV_SEMIHOSTING_HOSTFS=y</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/semihost#appendix-configure-nuttx-for-initial-ram-disk">(How we configured NuttX for RAM Disk)</a></p>
<p>Thats it! These are the files that we modified in NuttX QEMU to load the Initial RAM Disk (without Semihosting)…</p>
<ul>
<li><a href="https://github.com/lupyuen2/wip-nuttx/pull/33/files"><strong>Modified Files for NuttX QEMU with Initial RAM Disk</strong></a></li>
</ul>
<p><em>Whats ROMFS?</em></p>
<p><a href="https://en.wikipedia.org/wiki/Romfs"><strong>ROMFS</strong></a> is the <strong>Filesystem Format</strong> of our Initial RAM Disk. (It defines how the Files and Directories are stored in the RAM Disk)</p>
<p>We could have used a FAT or EXT4 or NTFS Filesystem… But ROMFS is a lot simpler for NuttX.</p>
<p><a href="https://nuttx.apache.org/docs/latest/components/filesystem.html">(More about <strong>ROMFS in NuttX</strong>)</a></p>
<p><em>Why did we copy the RAM Disk from <strong><code>0x8400</code> <code>0000</code></strong>?</em></p>
<p>QEMU loads the Initial RAM Disk into RAM at <strong><code>0x8400</code> <code>0000</code></strong></p>
<ul>
<li><a href="https://lupyuen.github.io/articles/semihost#appendix-ram-disk-address-for-risc-v-qemu"><strong>“RAM Disk Address for RISC-V QEMU”</strong></a></li>
</ul>
<p>Thats why we copied the RAM Disk from <strong><code>0x8400</code> <code>0000</code></strong> to <strong>ramdisk_start</strong>.</p>
<p><em>Wow how did we figure out all this?</em></p>
<p>Actually we had plenty of guidance from NuttX on <strong>LiteX Arty-A7</strong>. Heres our Detailed Analysis…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/semihost#appendix-initial-ram-disk-for-litex-arty-a7"><strong>“Initial RAM Disk for LiteX Arty-A7”</strong></a></li>
</ul>
<p><img src="https://lupyuen.github.io/images/semihost-runqemu.png" alt="Booting NuttX QEMU with Initial RAM Disk" /></p>
<h1 id="boot-nuttx-qemu-with-initial-ram-disk"><a class="doc-anchor" href="#boot-nuttx-qemu-with-initial-ram-disk">§</a>7 Boot NuttX QEMU with Initial RAM Disk</h1>
<p>Were ready to run our modified NuttX QEMU… That loads the Initial RAM Disk!</p>
<p>We build NuttX QEMU in Kernel Mode (as before). Then we generate the Initial RAM Disk <strong>initrd</strong></p>
<div class="example-wrap"><pre class="language-bash"><code>## Omitted: Build NuttX QEMU in Kernel Mode
...
## Omitted: Build Apps Filesystem for NuttX QEMU
...
## Generate the Initial RAM Disk `initrd`
## in ROMFS Filesystem Format
## from the Apps Filesystem `../apps/bin`
## and label it `NuttXBootVol`
genromfs \
-f initrd \
-d ../apps/bin \
-V &quot;NuttXBootVol&quot;</code></pre></div>
<p><a href="https://github.com/lupyuen2/wip-nuttx/releases/tag/ramdisk-0.0.1">(See the <strong>Build Steps</strong>)</a></p>
<p><a href="https://gist.github.com/lupyuen/394bc4da808ee5e4f5fb8da70cb2ae3e">(See the <strong>Build Log</strong>)</a></p>
<p><a href="https://manpages.ubuntu.com/manpages/trusty/man8/genromfs.8.html">(<strong>genromfs</strong> generates a <strong>ROM FS Filesystem</strong>)</a></p>
<p><a href="https://lupyuen.github.io/articles/romfs#inside-a-rom-fs-filesystem">(Inside a <strong>ROM FS Filesystem</strong>)</a></p>
<p>This creates an Initial RAM Disk <strong>initrd</strong> (in ROMFS format) thats 7.9 MB…</p>
<div class="example-wrap"><pre class="language-text"><code>$ ls -l initrd
-rw-r--r-- 1 7902208 initrd</code></pre></div>
<p><a href="https://github.com/lupyuen2/wip-nuttx/releases/tag/ramdisk-0.0.1">(See the <strong>Build Outputs</strong>)</a></p>
<p>Finally we start QEMU and <strong>load our Initial RAM Disk</strong></p>
<div class="example-wrap"><pre class="language-bash"><code>## Start NuttX on QEMU
## with Initial RAM Disk `initrd`
qemu-system-riscv64 \
-kernel nuttx \
-initrd initrd \
-cpu rv64 \
-M virt,aclint=on \
-semihosting \
-bios none \
-nographic</code></pre></div>
<p><a href="https://www.qemu.org/docs/master/system/riscv/virt.html#running-linux-kernel">(Source)</a></p>
<p>(Remove <strong><code>-bios none</code></strong> for newer versions of NuttX)</p>
<p>And NuttX QEMU boots OK with our Initial RAM Disk yay! (Ignore the warnings)</p>
<div class="example-wrap"><pre class="language-text"><code>ABC
nx_start: Entry
uart_register: Registering /dev/console
uart_register: Registering /dev/ttyS0
work_start_lowpri: Starting low-priority kernel worker thread(s)
board_late_initialize:
nx_start_application: Starting init task: /system/bin/init
elf_symname: Symbol has no name
elf_symvalue: SHN_UNDEF: Failed to get symbol name: -3
elf_relocateadd: Section 2 reloc 2: Undefined symbol[0] has no name: -3
up_exit: TCB=0x802088d0 exiting
NuttShell (NSH) NuttX-12.0.3
nsh&gt; nx_start: CPU0: Beginning Idle Loop
nsh&gt;</code></pre></div>
<p><a href="https://github.com/lupyuen2/wip-nuttx/releases/tag/ramdisk-0.0.1">(See the <strong>Run Log</strong>)</a></p>
<p><a href="https://gist.github.com/lupyuen/8afee5b07b61bb7f9f202f7f8c5e3ab3">(See the <strong>Detailed Run Log</strong>)</a></p>
<p>We see <strong>exec_spawn</strong> warnings like this…</p>
<div class="example-wrap"><pre class="language-text"><code>nsh&gt; ls -l /system/bin/init
posix_spawn: pid=0xc0202978 path=ls file_actions=0xc0202980 attr=0xc0202988 argv=0xc0202a28
exec_spawn: ERROR: Failed to load program &#39;ls&#39;: -2
nxposix_spawn_exec: ERROR: exec failed: 2
-r-xr-xr-x 3278720 /system/bin/init</code></pre></div>
<p>But its OK to ignore them, because “<strong><code>ls</code></strong>” is a built-in Shell Command.</p>
<p>(Not an Executable File from our Apps Filesystem)</p>
<p>Now that we figured out Initial RAM Disk on QEMU, lets do the same for Star64…</p>
<p><img src="https://lupyuen.github.io/images/semihost-title.jpg" alt="Booting NuttX on Star64 with Initial RAM Disk" /></p>
<h1 id="nuttx-star64-with-initial-ram-disk"><a class="doc-anchor" href="#nuttx-star64-with-initial-ram-disk">§</a>8 NuttX Star64 with Initial RAM Disk</h1>
<p>One last thing for today: Booting NuttX on <strong>Star64 with Initial RAM Disk</strong>! (Instead of Semihosting)</p>
<p>We modify NuttX Star64 with the exact same steps as <a href="https://lupyuen.github.io/articles/semihost#modify-nuttx-qemu-for-initial-ram-disk"><strong>NuttX QEMU with Initial RAM Disk</strong></a></p>
<ul>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx/pull/34/files"><strong>Modified Files for Initial RAM Disk on Star64</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx/pull/34/files#diff-a663261ea6b68497baecd83562df554d2c7903261090bf627042860d90fb920f"><strong>qemu_rv_mm_init.c</strong></a>: Copy RAM Disk at Startup</p>
</li>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx/pull/34/files#diff-beeaeb03fa5642002a542446c89251c9a7c5c1681cfe915387740ea0975e91b3"><strong>qemu_rv_appinit.c</strong></a>: Mount RAM Disk at Startup</p>
</li>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx/pull/34/files#diff-a1d53d0735749ccfb3072e986511d0b6cae6f7ce850d8c91195cc027201a0132"><strong>fs_romfsutil.c</strong></a>: Validate RAM Disk Bounds</p>
</li>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx/pull/34/files#diff-fbe356a2692accfbf05c87b4b1a3ecb7275bf38d06f9ceb7730928249f15d605"><strong>ld-kernel64.script</strong></a>: Linker Script with RAM Disk Memory</p>
</li>
<li>
<p><a href="https://github.com/lupyuen2/wip-nuttx/pull/34/files#diff-4018c37bf9b08236b37a84273281d5511d48596be9e0e4c0980d730aa95dbbe8"><strong>knsh64/defconfig</strong></a>: Build Configuration for RAM Disk</p>
<p><a href="https://lupyuen.github.io/articles/semihost#appendix-configure-nuttx-for-initial-ram-disk">(How we configured NuttX for RAM Disk)</a></p>
</li>
</ul>
<p>Note that we copy the Initial RAM Disk from <strong><code>0x4610</code> <code>0000</code></strong> (instead of QEMUs <code>0x8400</code> <code>0000</code>): <a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/jh7110/jh7110_mm_init.c#L268-L275">jh7110_mm_init.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>// Copy RAM Disk from 0x4610 0000 to
// `__ramdisk_start` (`__ramdisk_size` bytes)
// TODO: RAM Disk must not exceed `__ramdisk_size` bytes
memcpy( // Copy the RAM Disk...
(void *)__ramdisk_start, // To RAM Disk Memory
(void *)0x46100000, // From U-Boot initrd Address
(size_t)__ramdisk_size // For 16 MB
);</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/semihost#appendix-ram-disk-address-for-risc-v-qemu">(U-Boot Bootloader loads the RAM Disk at <strong><code>0x4610</code> <code>0000</code></strong>)</a></p>
<p>And the <strong>RAM Disk Memory</strong> is now located at <strong><code>0x40A0</code> <code>0000</code></strong> (the next available RAM Address): <a href="https://github.com/apache/nuttx/blob/master/boards/risc-v/jh7110/star64/scripts/ld.script#L20-L56">ld.script</a></p>
<div class="example-wrap"><pre class="language-text"><code>MEMORY
{
...
/* Added RAM Disk Memory (Max 16 MB) */
ramdisk (rwx) : ORIGIN = 0x40A00000, LENGTH = 16M /* w/ cache */
}
/* Increased Page Heap for RAM Disk */
__pgheap_size = LENGTH(pgram) + LENGTH(ramdisk);
/* Previously: __pgheap_size = LENGTH(pgram); */
/* Added RAM Disk Symbols */
__ramdisk_start = ORIGIN(ramdisk);
__ramdisk_size = LENGTH(ramdisk);
__ramdisk_end = ORIGIN(ramdisk) + LENGTH(ramdisk);</code></pre></div>
<p>The <a href="https://github.com/lupyuen2/wip-nuttx/pull/34/files"><strong>other modified files</strong></a> are the same as for NuttX QEMU with Initial RAM Disk.</p>
<p><a href="https://github.com/lupyuen/nuttx-star64#increase-ram-disk-limit">(How to increase the <strong>RAM Disk Limit</strong>)</a></p>
<p><a href="https://github.com/lupyuen/nuttx-star64#memory-map-for-ram-disk">(NuttX Apps are limited to <strong>4 MB RAM</strong>)</a></p>
<p><a href="https://github.com/lupyuen/nuttx-star64#increase-page-heap-size">(How to increase the <strong>Page Heap Size</strong>)</a></p>
<p><em>How do we run this on Star64?</em></p>
<p>We build NuttX Star64, generate the Initial RAM Disk <strong>initrd</strong> and copy to our TFTP Folder <a href="https://lupyuen.github.io/articles/semihost#appendix-boot-nuttx-over-tftp-with-initial-ram-disk">(for <strong>Network Booting</strong>)</a></p>
<div class="example-wrap"><pre class="language-bash"><code>## Omitted: Build NuttX Star64
...
## Omitted: Build Apps Filesystem for NuttX Star64
...
## Generate the Initial RAM Disk `initrd`
## in ROMFS Filesystem Format
## from the Apps Filesystem `../apps/bin`
## and label it `NuttXBootVol`
genromfs \
-f initrd \
-d ../apps/bin \
-V &quot;NuttXBootVol&quot;
## Copy NuttX Binary Image, Device Tree and
## Initial RAM Disk to TFTP Folder
cp nuttx.bin $HOME/tftproot/Image
cp jh7110-star64-pine64.dtb $HOME/tftproot
cp initrd $HOME/tftproot</code></pre></div>
<p><a href="https://github.com/lupyuen2/wip-nuttx/releases/tag/star64c-0.0.1">(See the <strong>Build Steps</strong>)</a></p>
<p><a href="https://gist.github.com/lupyuen/ae59a840c94280ce8d618699278a0436">(See the <strong>Build Log</strong>)</a></p>
<p><a href="https://manpages.ubuntu.com/manpages/trusty/man8/genromfs.8.html">(<strong>genromfs</strong> generates a <strong>ROM FS Filesystem</strong>)</a></p>
<p><a href="https://lupyuen.github.io/articles/romfs#inside-a-rom-fs-filesystem">(Inside a <strong>ROM FS Filesystem</strong>)</a></p>
<p>Our Initial RAM Disk <strong>initrd</strong> (with ROMFS inside) is 7.9 MB (slightly bigger)…</p>
<div class="example-wrap"><pre class="language-text"><code>$ ls -l initrd
-rw-r--r-- 1 7930880 initrd</code></pre></div>
<p><a href="https://github.com/lupyuen2/wip-nuttx/releases/tag/star64c-0.0.1">(See the <strong>Build Outputs</strong>)</a></p>
<p>And we boot NuttX on Star64 over TFTP or a microSD Card…</p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/semihost#appendix-boot-nuttx-over-tftp-with-initial-ram-disk"><strong>“Boot NuttX over TFTP with Initial RAM Disk”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/release#nuttx-in-a-bootable-microsd"><strong>“NuttX in a Bootable microSD”</strong></a></p>
</li>
</ul>
<p><em>Does it work?</em></p>
<p>Now Star64 JH7110 boots OK with the Initial RAM Disk yay! (Not completely though)</p>
<div class="example-wrap"><pre class="language-text"><code>Starting kernel ...
123067DFHBCI
nx_start: Entry
uart_register: Registering /dev/console
uart_register: Registering /dev/ttyS0
work_start_lowpri: Starting low-priority kernel worker thread(s)
board_late_initialize:
nx_start_application: Starting init task: /system/bin/init
elf_symname: Symbol has no name
elf_symvalue: SHN_UNDEF: Failed to get symbol name: -3
elf_relocateadd: Section 2 reloc 2: Undefined symbol[0] has no name: -3
nx_start_application: ret=3
up_exit: TCB=0x404088d0 exiting
nx_start: CPU0: Beginning Idle Loop</code></pre></div>
<p><a href="https://github.com/lupyuen2/wip-nuttx/releases/tag/star64c-0.0.1">(See the <strong>Output Log</strong>)</a></p>
<p>So many questions (pic below)…</p>
<ul>
<li>
<p>Why no <strong>NuttX Shell</strong>?</p>
<p>Was it started correctly?</p>
<p>(<a href="https://github.com/apache/nuttx/blob/master/sched/init/nx_bringup.c#L297-L299"><strong>nx_start_application</strong></a> returned Process ID 3, seems OK)</p>
</li>
<li>
<p>Is <strong>Console Output</strong> working in NuttX Shell?</p>
<p>(Highly sus!)</p>
</li>
<li>
<p>Is our <a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/jh7110/hardware/jh7110_memorymap.h#L27-L33"><strong>Interrupt Controller</strong></a> OK?</p>
<p><a href="https://github.com/apache/nuttx/blob/master/boards/risc-v/jh7110/star64/configs/nsh/defconfig#L10-L18">(See <strong>CONFIG_16550_UART0_IRQ</strong>)</a></p>
<p><a href="https://doc-en.rvspace.org/JH7110/TRM/JH7110_TRM/u74_memory_map.html">(See the <strong>JH7110 U74 Memory Map</strong>)</a></p>
</li>
<li>
<p>Are we using the right <a href="https://github.com/apache/nuttx/blob/master/boards/risc-v/jh7110/star64/include/board_memorymap.h#L33-L38"><strong>User Address Space</strong></a>?</p>
<p>And the right <a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/jh7110/jh7110_mm_init.c#L46-L51"><strong>I/O Address Space</strong></a>?</p>
</li>
<li>
<p>How to handle <a href="https://github.com/apache/nuttx/blob/master/arch/risc-v/src/qemu-rv/qemu_rv_timerisr.c#L151-L210"><strong>RISC-V Timers in Supervisor Mode</strong></a>?</p>
<p>Do we need <a href="https://github.com/riscv-non-isa/riscv-sbi-doc/blob/master/riscv-sbi.adoc#timer-extension-eid-0x54494d45-time"><strong>OpenSBI Timers</strong></a>?</p>
</li>
</ul>
<p>Well find out in the next article!</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/plic"><strong>“Star64 JH7110 + NuttX RTOS: RISC-V PLIC Interrupts and Serial I/O”</strong></a></li>
</ul>
<p><img src="https://lupyuen.github.io/images/semihost-runstar64.png" alt="NuttX Star64 with Initial RAM Disk" /></p>
<h1 id="whats-next"><a class="doc-anchor" href="#whats-next">§</a>9 Whats Next</h1>
<p>No more <strong>Semihosting Problems</strong> with NuttX on Star64 JH7110 SBC!</p>
<ul>
<li>
<p>We discovered that NuttX calls <strong>RISC-V Semihosting</strong></p>
<p>(To access the Apps Filesystem)</p>
</li>
<li>
<p>But it crashes <strong>NuttX on Star64</strong></p>
<p>(Because Semihosting wont work on Bare Metal)</p>
</li>
<li>
<p>NuttX Shell lives in the NuttX <strong>Apps Filesystem</strong></p>
<p>(So its mandatory for booting NuttX)</p>
</li>
<li>
<p>Thus we replaced Semihosting by <strong>Initial RAM Disk “initrd”</strong></p>
<p>(And it works on Star64!)</p>
</li>
<li>
<p>By adapting the code from NuttX on <strong>LiteX Arty-A7</strong></p>
<p>(Which we also tested on <strong>QEMU Emulator</strong>)</p>
</li>
<li>
<p>Now we need to figure out why <strong>NuttX Shell</strong> wont appear…</p>
<p><a href="https://lupyuen.github.io/articles/plic"><strong>“Star64 JH7110 + NuttX RTOS: RISC-V PLIC Interrupts and Serial I/O”</strong></a></p>
</li>
</ul>
<p>Many Thanks to my <a href="https://lupyuen.github.io/articles/sponsor"><strong>GitHub Sponsors</strong></a> for supporting my work! This article wouldnt have been possible without your support.</p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/sponsor"><strong>Sponsor me a coffee</strong></a></p>
</li>
<li>
<p><a href="https://news.ycombinator.com/item?id=36901287"><strong>Discuss this article on Hacker News</strong></a></p>
</li>
<li>
<p><a href="https://forum.pine64.org/showthread.php?tid=18551"><strong>Discuss this article on Pine64 Forum</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/nuttx-ox64"><strong>My Current Project: “Apache NuttX RTOS for Ox64 BL808”</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/nuttx-star64"><strong>My Other Project: “NuttX for Star64 JH7110”</strong></a></p>
</li>
<li>
<p><a href="https://github.com/lupyuen/pinephone-nuttx"><strong>Older Project: “NuttX for PinePhone”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io"><strong>Check out my articles</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/rss.xml"><strong>RSS Feed</strong></a></p>
</li>
</ul>
<p><em>Got a question, comment or suggestion? Create an Issue or submit a Pull Request here…</em></p>
<p><a href="https://github.com/lupyuen/lupyuen.github.io/blob/master/src/semihost.md"><strong>lupyuen.github.io/src/semihost.md</strong></a></p>
<p><img src="https://lupyuen.github.io/images/semihost-title.jpg" alt="Booting NuttX on Star64 with Initial RAM Disk" /></p>
<h1 id="appendix-boot-nuttx-over-tftp-with-initial-ram-disk"><a class="doc-anchor" href="#appendix-boot-nuttx-over-tftp-with-initial-ram-disk">§</a>10 Appendix: Boot NuttX over TFTP with Initial RAM Disk</h1>
<p>Previously we configured Star64s U-Boot Bootloader to <strong>boot NuttX over TFTP</strong></p>
<ul>
<li><a href="https://lupyuen.github.io/articles/tftp"><strong>“Star64 JH7110 RISC-V SBC: Boot from Network with U-Boot and TFTP”</strong></a></li>
</ul>
<p>Now we need to tweak the U-Boot Settings to boot with our <strong>Initial RAM Disk</strong>.</p>
<p>Star64s U-Boot Bootloader loads our Initial RAM Disk at <strong><code>0x4610</code> <code>0000</code></strong></p>
<div class="example-wrap"><pre class="language-bash"><code>ramdisk_addr_r=0x46100000</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/linux#u-boot-settings-for-star64">(Source)</a></p>
<p>Which means that we need to add these <strong>TFTP Commands</strong> to U-Boot Bootloader…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Added this: Assume Initial RAM Disk is max 16 MB
setenv ramdisk_size 0x1000000
printenv ramdisk_size
saveenv
## Load Kernel and Device Tree over TFTP
tftpboot ${kernel_addr_r} ${tftp_server}:Image
tftpboot ${fdt_addr_r} ${tftp_server}:jh7110-star64-pine64.dtb
fdt addr ${fdt_addr_r}
## Added this: Load Initial RAM Disk over TFTP
tftpboot ${ramdisk_addr_r} ${tftp_server}:initrd
## Changed this: Replaced `-` by `ramdisk_addr_r:ramdisk_size`
booti ${kernel_addr_r} ${ramdisk_addr_r}:${ramdisk_size} ${fdt_addr_r}</code></pre></div>
<p>Which will change our <strong>U-Boot Boot Script</strong> to…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Load the NuttX Image from TFTP Server
## kernel_addr_r=0x40200000
## tftp_server=192.168.x.x
if tftpboot ${kernel_addr_r} ${tftp_server}:Image;
then
## Load the Device Tree from TFTP Server
## fdt_addr_r=0x46000000
if tftpboot ${fdt_addr_r} ${tftp_server}:jh7110-star64-pine64.dtb;
then
## Set the RAM Address of Device Tree
## fdt_addr_r=0x46000000
if fdt addr ${fdt_addr_r};
then
## Load the Intial RAM Disk from TFTP Server
## ramdisk_addr_r=0x46100000
if tftpboot ${ramdisk_addr_r} ${tftp_server}:initrd;
then
## Boot the NuttX Image with the Initial RAM Disk and Device Tree
## kernel_addr_r=0x40200000
## ramdisk_addr_r=0x46100000
## ramdisk_size=0x1000000
## fdt_addr_r=0x46000000
booti ${kernel_addr_r} ${ramdisk_addr_r}:${ramdisk_size} ${fdt_addr_r};
fi;
fi;
fi;
fi</code></pre></div>
<p>Which becomes…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Assume Initial RAM Disk is max 16 MB
setenv ramdisk_size 0x1000000
## Check that it&#39;s correct
printenv ramdisk_size
## Save it for future reboots
saveenv
## Add the Boot Command for TFTP
setenv bootcmd_tftp &#39;if tftpboot ${kernel_addr_r} ${tftp_server}:Image ; then if tftpboot ${fdt_addr_r} ${tftp_server}:jh7110-star64-pine64.dtb ; then if fdt addr ${fdt_addr_r} ; then if tftpboot ${ramdisk_addr_r} ${tftp_server}:initrd ; then booti ${kernel_addr_r} ${ramdisk_addr_r}:${ramdisk_size} ${fdt_addr_r} ; fi ; fi ; fi ; fi&#39;
## Check that it&#39;s correct
printenv bootcmd_tftp
## Save it for future reboots
saveenv</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/tftp#configure-u-boot-for-tftp">(Remember to set <strong>tftp_server</strong> and <strong>boot_targets</strong>)</a></p>
<p>Run the above commands in U-Boot.</p>
<p>Copy the Initial RAM Disk <strong>initrd</strong> to the TFTP Folder…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Copy NuttX Binary Image, Device Tree and
## Initial RAM Disk to TFTP Folder
cp nuttx.bin $HOME/tftproot/Image
cp jh7110-star64-pine64.dtb $HOME/tftproot
cp initrd $HOME/tftproot</code></pre></div>
<p><a href="https://lupyuen.github.io/articles/semihost#nuttx-star64-with-initial-ram-disk">(Source)</a></p>
<p>Power Star64 off and on.</p>
<p>NuttX now boots with our Initial RAM Disk over TFTP…</p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/semihost#nuttx-star64-with-initial-ram-disk"><strong>“NuttX Star64 with Initial RAM Disk”</strong></a></p>
<p><a href="https://youtu.be/TdSJdiQFsv8">(Watch the Demo on YouTube)</a></p>
</li>
</ul>
<p>Heres the <strong>U-Boot Log</strong></p>
<div class="example-wrap"><pre class="language-text"><code>TFTP from server 192.168.x.x; our IP address is 192.168.x.x
Filename &#39;Image&#39;.
Load address: 0x40200000
Loading: 9 MiB/s
done
Bytes transferred = 2097800 (200288 hex)
Using ethernet@16030000 device
TFTP from server 192.168.x.x; our IP address is 192.168.x.x
Filename &#39;jh7110-star64-pine64.dtb&#39;.
Load address: 0x46000000
Loading: 8 MiB/s
done
Bytes transferred = 50235 (c43b hex)
Using ethernet@16030000 device
TFTP from server 192.168.x.x; our IP address is 192.168.x.x
Filename &#39;initrd&#39;.
Load address: 0x46100000
Loading: 371.1 KiB/s
done
Bytes transferred = 7930880 (790400 hex)
## Flattened Device Tree blob at 46000000
Booting using the fdt blob at 0x46000000
Using Device Tree in place at 0000000046000000, end 000000004600f43a
Starting kernel ...</code></pre></div>
<p><em>What if we omit the RAM Disk Size?</em></p>
<p>U-Boot wont boot NuttX if we omit the RAM Disk Size…</p>
<div class="example-wrap"><pre class="language-bash"><code>## If we omit RAM Disk Size:
## Boot Fails
$ booti ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r}
Wrong Ramdisk Image Format
Ramdisk image is corrupt or invalid</code></pre></div>
<p>So we hardcode a maximum RAM Disk Size of 16 MB…</p>
<div class="example-wrap"><pre class="language-bash"><code>## If we assume RAM Disk Size is max 16 MB:
## Boots OK
$ booti ${kernel_addr_r} ${ramdisk_addr_r}:0x1000000 ${fdt_addr_r}</code></pre></div>
<p>Lets talk about the NuttX Configuration for Initial RAM Disk…</p>
<p><img src="https://lupyuen.github.io/images/semihost-runstar64.png" alt="NuttX Star64 with Initial RAM Disk" /></p>
<h1 id="appendix-configure-nuttx-for-initial-ram-disk"><a class="doc-anchor" href="#appendix-configure-nuttx-for-initial-ram-disk">§</a>11 Appendix: Configure NuttX for Initial RAM Disk</h1>
<p>Earlier we configured NuttX QEMU and NuttX Star64 to boot with our <strong>Initial RAM Disk</strong></p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/semihost#modify-nuttx-qemu-for-initial-ram-disk"><strong>“NuttX QEMU with Initial RAM Disk”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/semihost#nuttx-star64-with-initial-ram-disk"><strong>“NuttX Star64 with Initial RAM Disk”</strong></a></p>
</li>
</ul>
<p>Here are the steps for updating the NuttX Build Configuration in <code>make menuconfig</code></p>
<ol>
<li>
<p>Board Selection &gt; Enable boardctl() interface &gt; Enable application space creation of ROM disks</p>
</li>
<li>
<p>RTOS Features &gt; RTOS hooks &gt; Custom board late initialization</p>
</li>
<li>
<p>File Systems &gt; ROMFS file system</p>
</li>
<li>
<p>RTOS Features &gt; Tasks and Scheduling &gt; Auto-mount init file system</p>
<p>Set to <code>/system/bin</code></p>
</li>
<li>
<p>Build Setup &gt; Debug Options &gt; File System Debug Features &gt; File System Error, Warnings and Info Output</p>
</li>
<li>
<p>Disable: File Systems &gt; Host File System</p>
</li>
<li>
<p>Manually delete from <a href="https://github.com/apache/nuttx/blob/master/boards/risc-v/jh7110/star64/configs/nsh/defconfig">nsh/defconfig</a></p>
<div class="example-wrap"><pre class="language-text"><code>CONFIG_HOST_MACOS=y
CONFIG_INIT_MOUNT_DATA=&quot;fs=../apps&quot;
CONFIG_INIT_MOUNT_FSTYPE=&quot;hostfs&quot;
CONFIG_INIT_MOUNT_SOURCE=&quot;&quot;</code></pre></div></li>
</ol>
<p>The steps above will produce the updated Build Configuration Files…</p>
<ul>
<li>
<p><strong>NuttX for QEMU:</strong> <a href="https://github.com/lupyuen2/wip-nuttx/blob/ramdisk/boards/risc-v/qemu-rv/rv-virt/configs/knsh64/defconfig"><strong>knsh64/defconfig</strong></a></p>
</li>
<li>
<p><strong>NuttX for Star64:</strong> <a href="https://github.com/apache/nuttx/blob/master/boards/risc-v/jh7110/star64/configs/nsh/defconfig"><strong>nsh/defconfig</strong></a></p>
</li>
</ul>
<h1 id="appendix-ram-disk-address-for-risc-v-qemu"><a class="doc-anchor" href="#appendix-ram-disk-address-for-risc-v-qemu">§</a>12 Appendix: RAM Disk Address for RISC-V QEMU</h1>
<p><em>We need the RAM Disk Address for RISC-V QEMU…</em></p>
<p><em>Can we enable logging for RISC-V QEMU?</em></p>
<p>Yep we use this QEMU Option: <strong><code>-trace "*"</code></strong></p>
<div class="example-wrap"><pre class="language-bash"><code>## Start NuttX on QEMU
## with Initial RAM Disk `initrd`
qemu-system-riscv64 \
-semihosting \
-M virt,aclint=on \
-cpu rv64 \
-bios none \
-kernel nuttx \
-initrd initrd \
-nographic \
-trace &quot;*&quot;</code></pre></div>
<p>(Remove <strong><code>-bios none</code></strong> for newer versions of NuttX)</p>
<p>In the QEMU Command above, we load the Initial RAM Disk <strong>initrd</strong>.</p>
<p>To discover the RAM Address of the Initial RAM Disk, we check the <strong>QEMU Trace Log</strong></p>
<div class="example-wrap"><pre class="language-text"><code>resettablloader_write_rom nuttx
ELF program header segment 0:
@0x80000000 size=0x2b374 ROM=0
loader_write_rom nuttx
ELF program header segment 1:
@0x80200000 size=0x2a1 ROM=0
loader_write_rom initrd:
@0x84000000 size=0x2fc3e8 ROM=0
loader_write_rom fdt:
@0x87000000 size=0x100000 ROM=0</code></pre></div>
<p>This says that QEMU loads our Initial RAM Disk <strong>initrd</strong> at <strong><code>0x8400</code> <code>0000</code></strong></p>
<p>(And QEMU loads our Kernel at <strong><code>0x8000</code> <code>0000</code></strong>, Device Tree at <strong><code>0x8700</code> <code>0000</code></strong>)</p>
<p>We set the RAM Address of the Initial RAM Disk here…</p>
<ul>
<li><a href="https://lupyuen.github.io/articles/semihost#modify-nuttx-qemu-for-initial-ram-disk"><strong>“Modify NuttX QEMU for Initial RAM Disk”</strong></a></li>
</ul>
<p>We thought the Initial RAM Disk Address could be discovered from the Device Tree for RISC-V QEMU. But nope its not there…</p>
<h1 id="appendix-device-tree-for-risc-v-qemu"><a class="doc-anchor" href="#appendix-device-tree-for-risc-v-qemu">§</a>13 Appendix: Device Tree for RISC-V QEMU</h1>
<p>To dump the Device Tree for RISC-V QEMU, we specify <strong><code>dumpdtb</code></strong></p>
<div class="example-wrap"><pre class="language-bash"><code>## Dump Device Tree for RISC-V QEMU
## Remove `-bios none` for newer versions of NuttX
qemu-system-riscv64 \
-semihosting \
-M virt,aclint=on,dumpdtb=qemu-riscv64.dtb \
-cpu rv64 \
-bios none \
-kernel nuttx \
-nographic
## Convert Device Tree to text format
dtc \
-o qemu-riscv64.dts \
-O dts \
-I dtb \
qemu-riscv64.dtb</code></pre></div>
<p><a href="https://manpages.ubuntu.com/manpages/xenial/man1/dtc.1.html">(<strong>dtc</strong> decompiles a Device Tree)</a></p>
<p>This produces the Device Tree for RISC-V QEMU…</p>
<ul>
<li><a href="https://github.com/lupyuen/nuttx-star64/blob/main/qemu-riscv64.dts"><strong>qemu-riscv64.dts: Device Tree for RISC-V QEMU</strong></a></li>
</ul>
<p>Which is helpful for browsing the Memory Addresses of I/O Peripherals in QEMU.</p>
<h1 id="appendix-initial-ram-disk-for-litex-arty-a7"><a class="doc-anchor" href="#appendix-initial-ram-disk-for-litex-arty-a7">§</a>14 Appendix: Initial RAM Disk for LiteX Arty-A7</h1>
<p>Earlier we modified NuttX QEMU and NuttX Star64 to load our <strong>Initial RAM Disk</strong></p>
<ul>
<li>
<p><a href="https://lupyuen.github.io/articles/semihost#modify-nuttx-qemu-for-initial-ram-disk"><strong>“NuttX QEMU with Initial RAM Disk”</strong></a></p>
</li>
<li>
<p><a href="https://lupyuen.github.io/articles/semihost#nuttx-star64-with-initial-ram-disk"><strong>“NuttX Star64 with Initial RAM Disk”</strong></a></p>
</li>
</ul>
<p>We did it with plenty of guidance from NuttX on <strong>LiteX Arty-A7</strong>, below is our Detailed Analysis.</p>
<p>To <strong>generate the RAM Disk</strong> for LiteX Arty-A7, we run this command…</p>
<div class="example-wrap"><pre class="language-bash"><code>## Generate the Initial RAM Disk `romfs.img`
## in ROMFS Filesystem Format
## from the Apps Filesystem `../apps/bin`
## and label it `NuttXBootVol`
genromfs \
-f romfs.img \
-d ../apps/bin \
-V &quot;NuttXBootVol&quot;</code></pre></div>
<p><a href="https://nuttx.apache.org/docs/latest/platforms/risc-v/litex/cores/vexriscv_smp/index.html">(Source)</a></p>
<p><a href="https://manpages.ubuntu.com/manpages/trusty/man8/genromfs.8.html">(About <strong>genromfs</strong>)</a></p>
<p><a href="https://lupyuen.github.io/articles/romfs#inside-a-rom-fs-filesystem">(Inside a <strong>ROM FS Filesystem</strong>)</a></p>
<p><a href="https://cwiki.apache.org/confluence/plugins/servlet/mobile?contentId=139629548#content/view/139629548">(About NuttX RAM Disks and ROM Disks)</a></p>
<p><a href="https://nuttx.apache.org/docs/latest/platforms/risc-v/litex/cores/vexriscv_smp/index.html#booting"><strong>LiteX Memory Map</strong></a> tells us where the RAM Disk is loaded: <strong><code>0x40C0</code> <code>0000</code></strong></p>
<div class="example-wrap"><pre class="language-text"><code>&quot;romfs.img&quot;: &quot;0x40C00000&quot;,
&quot;nuttx.bin&quot;: &quot;0x40000000&quot;,
&quot;opensbi.bin&quot;: &quot;0x40f00000&quot;</code></pre></div>
<p>This is the <strong>LiteX Build Configuration</strong> for mounting the RAM Disk: <a href="https://github.com/apache/nuttx/blob/master/boards/risc-v/litex/arty_a7/configs/knsh/defconfig">knsh/defconfig</a></p>
<div class="example-wrap"><pre class="language-bash"><code>CONFIG_BOARDCTL_ROMDISK=y
CONFIG_BOARD_LATE_INITIALIZE=y
CONFIG_BUILD_KERNEL=y
CONFIG_FS_ROMFS=y
CONFIG_INIT_FILEPATH=&quot;/system/bin/init&quot;
CONFIG_INIT_MOUNT=y
CONFIG_INIT_MOUNT_FLAGS=0x1
CONFIG_INIT_MOUNT_TARGET=&quot;/system/bin&quot;
CONFIG_LITEX_APPLICATION_RAMDISK=y
CONFIG_NSH_FILE_APPS=y
CONFIG_NSH_READLINE=y
CONFIG_PATH_INITIAL=&quot;/system/bin&quot;
CONFIG_RAM_SIZE=4194304
CONFIG_RAM_START=0x40400000
CONFIG_RAW_BINARY=y
CONFIG_SYSTEM_NSH_PROGNAME=&quot;init&quot;
CONFIG_TESTING_GETPRIME=y</code></pre></div>
<p>Which is consistent with the NuttX Doc on <a href="https://nuttx.apache.org/docs/latest/applications/nsh/nsh.html#nsh-start-up-script"><strong>NSH Start-Up Script</strong></a></p>
<div class="example-wrap"><pre class="language-text"><code>CONFIG_DISABLE_MOUNTPOINT not set
CONFIG_FS_ROMFS enabled</code></pre></div>
<p>We mount the RAM Disk at <strong>LiteX Startup</strong>: <a href="https://github.com/apache/nuttx/blob/master/boards/risc-v/litex/arty_a7/src/litex_appinit.c#L76-L103">litex_appinit.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>void board_late_initialize(void)
{
#ifdef CONFIG_LITEX_APPLICATION_RAMDISK
litex_mount_ramdisk();
#endif
litex_bringup();
}</code></pre></div>
<p>Which calls <strong>litex_mount_ramdisk</strong> to mount the RAM Disk: <a href="https://github.com/apache/nuttx/blob/master/boards/risc-v/litex/arty_a7/src/litex_ramdisk.c#L41-L98">litex_ramdisk.c</a></p>
<div class="example-wrap"><pre class="language-c"><code>#ifndef CONFIG_BUILD_KERNEL
#error &quot;Ramdisk usage is intended to be used with kernel build only&quot;
#endif
#define SECTORSIZE 512
#define NSECTORS(b) (((b) + SECTORSIZE - 1) / SECTORSIZE)
#define RAMDISK_DEVICE_MINOR 0
// Mount a ramdisk defined in the ld-kernel.script to /dev/ramX.
// The ramdisk is intended to contain a romfs with applications which can
// be spawned at runtime.
int litex_mount_ramdisk(void)
{
int ret;
struct boardioc_romdisk_s desc;
desc.minor = RAMDISK_DEVICE_MINOR;
desc.nsectors = NSECTORS((ssize_t)__ramdisk_size);
desc.sectsize = SECTORSIZE;
desc.image = __ramdisk_start;
ret = boardctl(BOARDIOC_ROMDISK, (uintptr_t)&amp;desc);
if (ret &lt; 0)
{
syslog(LOG_ERR, &quot;Ramdisk register failed: %s\n&quot;, strerror(errno));
syslog(LOG_ERR, &quot;Ramdisk mountpoint /dev/ram%d\n&quot;,
RAMDISK_DEVICE_MINOR);
syslog(LOG_ERR, &quot;Ramdisk length %u, origin %x\n&quot;,
(ssize_t)__ramdisk_size,
(uintptr_t)__ramdisk_start);
}
return ret;
}</code></pre></div>
<p><strong>ramdisk_start</strong> and <strong>ramdisk_size</strong> are defined in the <strong>LiteX Memory Map</strong>: <a href="https://github.com/apache/nuttx/blob/master/boards/risc-v/litex/arty_a7/include/board_memorymap.h#L58-L91">board_memorymap.h</a></p>
<div class="example-wrap"><pre class="language-c"><code>/* RAMDisk */
#define RAMDISK_START (uintptr_t)__ramdisk_start
#define RAMDISK_SIZE (uintptr_t)__ramdisk_size
/* ramdisk (RW) */
extern uint8_t __ramdisk_start[];
extern uint8_t __ramdisk_size[];</code></pre></div>
<p>And also in the <strong>LiteX Linker Script</strong>: <a href="https://github.com/apache/nuttx/blob/master/boards/risc-v/litex/arty_a7/scripts/ld-kernel.script#L20-L49">ld-kernel.script</a></p>
<div class="example-wrap"><pre class="language-text"><code>MEMORY
{
kflash (rx) : ORIGIN = 0x40000000, LENGTH = 4096K /* w/ cache */
ksram (rwx) : ORIGIN = 0x40400000, LENGTH = 4096K /* w/ cache */
pgram (rwx) : ORIGIN = 0x40800000, LENGTH = 4096K /* w/ cache */
ramdisk (rwx) : ORIGIN = 0x40C00000, LENGTH = 4096K /* w/ cache */
}
...
/* Page heap */
__pgheap_start = ORIGIN(pgram);
__pgheap_size = LENGTH(pgram) + LENGTH(ramdisk);
/* Application ramdisk */
__ramdisk_start = ORIGIN(ramdisk);
__ramdisk_size = LENGTH(ramdisk);
__ramdisk_end = ORIGIN(ramdisk) + LENGTH(ramdisk);</code></pre></div>
<p>Note that <strong>pgheap_size</strong> needs to include <strong>ramdisk</strong> size.</p>
<!-- Begin scripts/rustdoc-after.html: Post-HTML for Custom Markdown files processed by rustdoc, like chip8.md -->
<!-- Begin Theme Picker and Prism Theme -->
<script src="../theme.js"></script>
<script src="../prism.js"></script>
<!-- Theme Picker and Prism Theme -->
<!-- End scripts/rustdoc-after.html -->
</body>
</html>