mirror of
https://github.com/lupyuen/lupyuen.github.io.git
synced 2025-01-13 10:18:33 +08:00
1098 lines
No EOL
62 KiB
HTML
1098 lines
No EOL
62 KiB
HTML
<!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="What’s Next">9 What’s 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 we’re 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>What’s <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>Let’s 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 it’s 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>What’s 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 we’re 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>(We’ll 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 doesn’t do anything meaningful!</p>
|
||
<p>Let’s 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>What’s 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? Let’s find out…</p>
|
||
<h1 id="nuttx-apps-filesystem"><a class="doc-anchor" href="#nuttx-apps-filesystem">§</a>4 NuttX Apps Filesystem</h1>
|
||
<p><em>What’s /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 what’s <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 \
|
||
>init.S \
|
||
2>&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>Isn’t 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=""
|
||
CONFIG_INIT_FILEPATH="/system/bin/init"
|
||
CONFIG_INIT_MOUNT=y
|
||
CONFIG_INIT_MOUNT_SOURCE=""
|
||
CONFIG_INIT_MOUNT_TARGET="/system"
|
||
CONFIG_INIT_MOUNT_FSTYPE="hostfs"
|
||
CONFIG_INIT_MOUNT_FLAGS=0x1
|
||
CONFIG_INIT_MOUNT_DATA="fs=../apps"
|
||
CONFIG_PATH_INITIAL="/system/bin"
|
||
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>That’s why it appears as <strong>/system/bin/init</strong>!</p>
|
||
<p><em>What’s 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 <main>:
|
||
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 it’s 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> doesn’t exist in the Semihosting Filesystem on Star64!</p>
|
||
</li>
|
||
</ol>
|
||
<p>This is why Semihosting won’t 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 won’t 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>That’s 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 won’t work on Star64?</em></p>
|
||
<p>Semihosting won’t work because NuttX for Star64 runs on <strong>Real SBC Hardware</strong> (Bare Metal)…</p>
|
||
<p>There’s 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), there’s something cool called an <a href="https://en.wikipedia.org/wiki/Initial_ramdisk"><strong>Initial RAM Disk (initrd)</strong></a>…</p>
|
||
<ul>
|
||
<li>
|
||
<p>It’s a <strong>RAM Disk</strong>, located in RAM (pic above)</p>
|
||
</li>
|
||
<li>
|
||
<p>But it’s an <strong>Initial</strong> RAM Disk. Which means there’s a Filesystem inside, preloaded with Files and Directories.</p>
|
||
</li>
|
||
</ul>
|
||
<p>Perfect for our NuttX Apps Filesystem!</p>
|
||
<p><em>That’s 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 it’s an <strong>Initial</strong> RAM Disk. Which means there’s 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, "/proc", "procfs", 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)&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're reading beyond the bounds of
|
||
// RAM Disk Memory, halt (and catch fire)
|
||
DEBUGASSERT(
|
||
&rm->rm_buffer[ndx] <
|
||
__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="/system/bin/init"
|
||
CONFIG_INIT_MOUNT=y
|
||
CONFIG_INIT_MOUNT_FLAGS=0x1
|
||
CONFIG_INIT_MOUNT_TARGET="/system/bin"
|
||
|
||
## 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>That’s 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>What’s 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>That’s 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>. Here’s 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>We’re 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 "NuttXBootVol"</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) that’s 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> nx_start: CPU0: Beginning Idle Loop
|
||
nsh></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> 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 'ls': -2
|
||
nxposix_spawn_exec: ERROR: exec failed: 2
|
||
-r-xr-xr-x 3278720 /system/bin/init</code></pre></div>
|
||
<p>But it’s 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, let’s 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 QEMU’s <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 "NuttXBootVol"
|
||
|
||
## 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>We’ll 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 What’s 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 won’t work on Bare Metal)</p>
|
||
</li>
|
||
<li>
|
||
<p>NuttX Shell lives in the NuttX <strong>Apps Filesystem</strong></p>
|
||
<p>(So it’s 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> won’t 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 wouldn’t 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 Star64’s 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>Star64’s 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's correct
|
||
printenv ramdisk_size
|
||
## Save it for future reboots
|
||
saveenv
|
||
|
||
## Add the Boot Command for TFTP
|
||
setenv bootcmd_tftp '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'
|
||
## Check that it'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>Here’s 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 'Image'.
|
||
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 'jh7110-star64-pine64.dtb'.
|
||
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 'initrd'.
|
||
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 won’t 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>Let’s 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 > Enable boardctl() interface > Enable application space creation of ROM disks</p>
|
||
</li>
|
||
<li>
|
||
<p>RTOS Features > RTOS hooks > Custom board late initialization</p>
|
||
</li>
|
||
<li>
|
||
<p>File Systems > ROMFS file system</p>
|
||
</li>
|
||
<li>
|
||
<p>RTOS Features > Tasks and Scheduling > Auto-mount init file system</p>
|
||
<p>Set to <code>/system/bin</code></p>
|
||
</li>
|
||
<li>
|
||
<p>Build Setup > Debug Options > File System Debug Features > File System Error, Warnings and Info Output</p>
|
||
</li>
|
||
<li>
|
||
<p>Disable: File Systems > 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="fs=../apps"
|
||
CONFIG_INIT_MOUNT_FSTYPE="hostfs"
|
||
CONFIG_INIT_MOUNT_SOURCE=""</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 "*"</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 it’s 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 "NuttXBootVol"</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>"romfs.img": "0x40C00000",
|
||
"nuttx.bin": "0x40000000",
|
||
"opensbi.bin": "0x40f00000"</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="/system/bin/init"
|
||
CONFIG_INIT_MOUNT=y
|
||
CONFIG_INIT_MOUNT_FLAGS=0x1
|
||
CONFIG_INIT_MOUNT_TARGET="/system/bin"
|
||
CONFIG_LITEX_APPLICATION_RAMDISK=y
|
||
CONFIG_NSH_FILE_APPS=y
|
||
CONFIG_NSH_READLINE=y
|
||
CONFIG_PATH_INITIAL="/system/bin"
|
||
CONFIG_RAM_SIZE=4194304
|
||
CONFIG_RAM_START=0x40400000
|
||
CONFIG_RAW_BINARY=y
|
||
CONFIG_SYSTEM_NSH_PROGNAME="init"
|
||
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 "Ramdisk usage is intended to be used with kernel build only"
|
||
#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)&desc);
|
||
if (ret < 0)
|
||
{
|
||
syslog(LOG_ERR, "Ramdisk register failed: %s\n", strerror(errno));
|
||
syslog(LOG_ERR, "Ramdisk mountpoint /dev/ram%d\n",
|
||
RAMDISK_DEVICE_MINOR);
|
||
syslog(LOG_ERR, "Ramdisk length %u, origin %x\n",
|
||
(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> |