<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://archfx.me/feed.xml" rel="self" type="application/atom+xml" /><link href="https://archfx.me/" rel="alternate" type="text/html" /><updated>2026-02-26T17:27:55-08:00</updated><id>https://archfx.me/feed.xml</id><title type="html">Aruna</title><subtitle>I am involved in research in hardware security validation and verification. My interests lie at the thin intersection of Electronics Engineering and Computer Science Engineering. Currently, I&apos;m working on hardware validation, test generation, and side-channel analysis. Some of the projects which are not affiliated are available for the public at my github. I have worked with a variety of CAD tools during various types of projects. Synopsys tools (VCS, DC, TMAX), Xilinx tools (Vivado, ISE), Intel Quartus, ModelSim, Icarus Verilog, Yosys, Altium, and Solidworks are a few of the tools. I am an automotive enthusiast who enjoys working on cars, and drones and taking them to the field to see how they perform. In my leisure time, I swim, ride kayaks, and involve in automotive stuff.</subtitle><author><name>A·ru·nạ</name></author><entry><title type="html">Instructor’s Guide to GitHub Classroom</title><link href="https://archfx.me/posts/2025/12/classroom/" rel="alternate" type="text/html" title="Instructor’s Guide to GitHub Classroom" /><published>2025-12-12T00:00:00-08:00</published><updated>2025-12-12T00:00:00-08:00</updated><id>https://archfx.me/posts/2025/12/blog-post-27</id><content type="html" xml:base="https://archfx.me/posts/2025/12/classroom/"><![CDATA[<p>Version control is a foundational skill in modern software development, and Git remains the most influential tool in this space. This post provides a brief history of Git, an overview of the ecosystem of platforms built around it, and a short, instructor-focused guide to using GitHub Classroom for managing programming assignments.</p>

<h2 id="from-the-linux-kernel-to-git">From the Linux Kernel to Git</h2>

<p>Git’s origins trace back to the early development of the <strong>Linux kernel</strong>, which started as a personal project by <a href="https://github.com/torvalds">Linus Torvalds</a> in the early 1990s. As the kernel community expanded, the need for an efficient, distributed version control system became increasingly urgent. Earlier tools were centralized, slow, or proprietary, none suited the rapid development the project required.</p>

<p><img src="/images/posts/git_branches.jpeg" /><br />
<small>Git Branches : credits to <a href="https://deepai.org/">deepAI</a></small></p>

<p>By 2005, after the Linux community lost access to BitKeeper (their then-primary tool), Torvalds created Git. It was designed in days but engineered with long-term principles that still define it today. It can easily,</p>

<ul>
  <li><strong>Handle massive, fast-moving projects</strong> like the Linux kernel.</li>
  <li><strong>Use a distributed model</strong> so every developer has the full history.</li>
  <li><strong>Ensure integrity</strong> using cryptographic hashing (SHA-1).</li>
  <li><strong>Make branching and merging extremely fast</strong>, encouraging experimentation.</li>
</ul>

<p>Git’s design made it not just a replacement for existing tools but a revolutionary new approach to version control.</p>

<h2 id="evolving-into-an-ecosystem">Evolving Into an Ecosystem</h2>

<p><a href=""><img alt="github" title="github" height="68" width="68" align="right" src="/images/icons/bitbucket.svg" /></a>
<a href=""><img alt="github" title="github" height="68" width="68" align="right" src="/images/icons/gitlab.svg" /></a>
<a href=""><img alt="github" title="github" height="68" width="68" align="right" src="/images/icons/github.svg" /></a></p>

<p>While Git itself is purely a command-line tool, hosting platforms transformed it into a global collaboration system. These platforms add issue trackers, pull requests, continuous integration, and social features, turning code repositories into complete development hubs. <a href="https://github.com/">GitHub</a>, launched in 2008, popularized “social coding” by introducing pull requests for structured code review, issues and project boards for lightweight project management, and community-driven features like forks and stars, along with integrated services such as GitHub Pages and Actions, helping it become the dominant open-source platform. <a href="https://gitlab.com/">GitLab</a> took a more open and automation-focused approach, offering a fully open-source, self-hostable Git server with built-in CI/CD pipelines and DevSecOps tooling designed for full lifecycle control. <a href="https://bitbucket.org/">Bitbucket</a> differentiated itself through early support for free private repositories and deep integration with Atlassian tools like Jira, Confluence, and Trello, while also providing both cloud and on-premise hosting. Together, these platforms illustrate how Git’s flexible design enabled a diverse and extensible ecosystem of collaboration tools.</p>

<h2 id="github-classroom">GitHub Classroom</h2>

<p>My main objective with this post is to introduce the <a href="https://classroom.github.com/">GitHub Classroom</a>. This extends GitHub for educational use, making it easier for instructors to distribute, manage, and grade programming assignments. Instead of manually creating repositories for each student, Classroom automates the entire workflow. Specially it has lot of very good features like,</p>

<ul>
  <li><strong>Exposes students to industrial workflows</strong>, preparing them for internships and industry work.</li>
  <li><strong>Automatically generates private student repositories</strong> with one link.</li>
  <li><strong>Supports starter code</strong>, allowing uniform project scaffolds.</li>
  <li><strong>Autograding</strong>, using tests instructors provide.</li>
  <li><strong>Simplifies feedback</strong>, since instructors can comment directly in PRs.</li>
</ul>

<p>It brings professional tooling into the classroom with minimal overhead.</p>

<h2 id="setting-up-a-github-classroom">Setting Up a GitHub Classroom</h2>

<p>Before getting started, you’ll need an active GitHub account. If you register using your university email address, you may be eligible for a <strong>free GitHub Pro</strong> or <strong>GitHub Education</strong> account, which provides additional features useful for teaching.</p>

<ul>
  <li><a href="https://github.com/education/teachers">Teachers – GitHub Education</a></li>
  <li><a href="https://education.github.com/pack">GitHub Student Developer Pack</a></li>
</ul>

<p>The steps below provide a high-level overview of how to set up GitHub Classroom effectively. This isn’t an exhaustive guide, but it covers the essential actions required to create and manage a functional course environment.</p>

<h3 id="create-or-choose-a-github-organization">Create or Choose a GitHub Organization</h3>

<p>A GitHub Organization acts as a container for all student repositories. Keeping course repositories organized in an organization avoids clutter in your personal GitHub account.</p>

<p><strong>Steps to create an organization:</strong></p>
<ol>
  <li>Visit <a href="https://github.com/organizations/new"><strong>github.com/organizations</strong></a></li>
  <li>Select a proper name for your class room</li>
  <li>Choose the free plan (GitHub upgrades education orgs automatically)</li>
  <li>Add co-instructors as owners or members</li>
</ol>

<p>This enables GitHub Classroom to automatically create student repos inside the organization .</p>

<h3 id="create-a-classroom">Create a Classroom</h3>

<p>The classroom provides a dashboard where assignments, student progress, and settings live.</p>

<p><strong>To create a classroom:</strong></p>
<ol>
  <li>Go to <a href="https://classroom.github.com/"><strong>classroom.github.com</strong></a></li>
  <li>Click <strong>New Classroom</strong></li>
  <li>Select your organization</li>
  <li>Name your classroom (e.g., <em>CPEN4700 – Fall 2025</em>)</li>
</ol>

<p>This will create a classroom dash board where you can add a class rooster. You can easily download the class rooster an CSV file and add it to your classroom.</p>

<h3 id="create-an-assignment">Create an Assignment</h3>

<p>Assignments are the core of GitHub Classroom. They can be configured as individual or group-based tasks and may include starter code, test suites, or autograding setups. Before you create an assignment, you’ll first need a <strong>template repository</strong>, this is the code scaffold that Classroom will copy into each student’s private repository.</p>

<p>In my classes, I typically provide a small sample project (such as a simple calculator) that students can complete in 5-10 minutes. This helps them get comfortable with the environment, the workflow, and the submission process.</p>

<p align="center">
  <a href="https://github.com/ArchitectLab/calculator">
    <img src="https://gh-card.dev/repos/ArchitectLab/calculator.svg?fullname=" />
  </a>
</p>

<p>If you’re interested, you’re welcome to use my sample project template repository to test your own assignment setup.</p>

<p><strong>Steps to create an assignment:</strong></p>
<ol>
  <li>In your classroom, click <strong>New Assignment</strong>.</li>
  <li>Choose either an <strong>Individual</strong> or <strong>Group</strong> assignment.</li>
  <li>Select the template repository for your starter code, such as<br />
<a href="https://github.com/ArchitectLab/calculator">“ArchitectLab/calculator”</a>, or create your own.</li>
  <li>Configure the assignment settings, including:
    <ul>
      <li>Deadlines</li>
      <li>Autograding tests</li>
      <li>Visibility</li>
    </ul>
  </li>
  <li>Generate the <strong>student invitation link</strong>.</li>
</ol>

<p>You can share this invitation link through Canvas announcements or within your project description. When students click the link for the first time, they will be prompted to select their name from the roster, which links their Canvas identity to their GitHub account. After this step, GitHub Classroom automatically creates a <strong>private repository</strong> for each student (or team). From the Classroom dashboard, you can easily monitor all student repositories and track their submissions in one place.</p>

<p>GitHub Classroom includes lightweight tools to help instructors keep track of submissions and performance.</p>

<p><strong>Monitoring features include:</strong></p>
<ul>
  <li>A list showing who has accepted the assignment</li>
  <li>Quick links to each student’s repository</li>
  <li>Autograding results (when enabled)</li>
  <li>Ability to leave inline comments or review pull requests</li>
</ul>

<p>This makes it easy to scale beyond small classes while maintaining feedback quality.</p>

<h3 id="complex-autograding-scripts">Complex AutoGrading Scripts</h3>

<p>By default, the autograding feature in GitHub Classroom allows you to test student submissions using predefined test cases and assign grades automatically. However, for more complex assignments, you can create custom workflows using <a href="https://github.com/features/actions">GitHub Actions</a>. These workflows let you automate the execution environment (including containerized setups) and evaluate student submissions with custom scripts.</p>

<p>For example, the following GitHub Actions workflow is used in my sample project to check the output produced by students’ programs:</p>

<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">name</span><span class="pi">:</span> <span class="s">Autograding Tests</span>

<span class="na">on</span><span class="pi">:</span>
  <span class="na">push</span><span class="pi">:</span>
  <span class="na">repository_dispatch</span><span class="pi">:</span>

<span class="na">permissions</span><span class="pi">:</span>
  <span class="na">contents</span><span class="pi">:</span> <span class="s">read</span>

<span class="na">jobs</span><span class="pi">:</span>
  <span class="na">run-autograding-tests</span><span class="pi">:</span>
    <span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
    <span class="na">if</span><span class="pi">:</span> <span class="s">github.actor != 'github-classroom[bot]'</span>
    <span class="na">steps</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Checkout code</span>
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/checkout@v4</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Prep</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">rm -f output.txt</span>
          <span class="s">chmod +x run.sh</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Run student script</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">runscript</span>
        <span class="na">shell</span><span class="pi">:</span> <span class="s">bash</span>
        <span class="na">run</span><span class="pi">:</span> <span class="s">./run.sh</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Check output with diff</span>
        <span class="na">id</span><span class="pi">:</span> <span class="s">check-diff</span>
        <span class="na">run</span><span class="pi">:</span> <span class="pi">|</span>
          <span class="s">echo "Comparing output.txt with expected_output.txt..."</span>
          <span class="s">if diff -wB output.txt expected_output.txt; then</span>
            <span class="s">echo "Output matches expected (0)"</span>
          <span class="s">else</span>
            <span class="s">echo "Output does not match (X)"</span>
            <span class="s">exit 1</span>
          <span class="s">fi</span>

      <span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Upload output artifact if output does not match</span>
        <span class="na">if</span><span class="pi">:</span> <span class="s">failure()</span>   
        <span class="na">uses</span><span class="pi">:</span> <span class="s">actions/upload-artifact@v4</span>
        <span class="na">with</span><span class="pi">:</span>
          <span class="na">name</span><span class="pi">:</span> <span class="s">student-output</span>
          <span class="na">path</span><span class="pi">:</span> <span class="pi">|</span>
            <span class="s">output.txt</span>
            <span class="s">expected_output.txt</span>
          <span class="na">retention-days</span><span class="pi">:</span> <span class="m">7</span>
</code></pre></div></div>

<p>This workflow can be easily extended to support more complex grading scenarios. Instead of running on the default <code class="language-plaintext highlighter-rouge">ubuntu-latest</code> runner, the job can be configured to execute inside a <strong>custom Docker container</strong> by specifying a <code class="language-plaintext highlighter-rouge">container</code> image, ensuring a consistent runtime environment across all submissions. The testing logic can also be customized by replacing <code class="language-plaintext highlighter-rouge">run.sh</code> with language-specific scripts, test frameworks (such as JUnit, pytest, or custom bash scripts), or multiple test stages. Additional steps can be added to install dependencies, run static analysis, or evaluate performance constraints. Together, these modifications allow instructors to tailor the autograding process to match real-world development environments and more advanced assignment requirements.</p>

<h2 id="final-thoughts">Final Thoughts</h2>

<p>Git was originally created to meet the fast-paced demands of early Linux kernel development, but it has since grown into a universal tool that underpins nearly all modern software engineering workflows. Platforms like GitHub expanded Git into a collaborative ecosystem, and GitHub Classroom builds on that foundation to support teaching and learning.</p>

<p>In summary, GitHub Classroom provides:</p>
<ul>
  <li>Professional-grade tools for students and exposure to CICD workflows</li>
  <li>Automated workflows that significantly reduce overhead</li>
  <li>A scalable, organized system for managing assignments</li>
</ul>

<p>Whether you’re teaching an introductory programming course or an upper-level computer engineering or computer science class, GitHub Classroom offers a simple yet powerful way to integrate real-world development practices into your curriculum. And based on student feedback, I can confidently say, they enjoy working with it just as much as we do.</p>]]></content><author><name>A·ru·nạ</name></author><category term="GitHub" /><category term="Classroom" /><category term="Autograder" /><summary type="html"><![CDATA[Version control is a foundational skill in modern software development, and Git remains the most influential tool in this space. This post provides a brief history of Git, an overview of the ecosystem of platforms built around it, and a short, instructor-focused guide to using GitHub Classroom for managing programming assignments.]]></summary></entry><entry><title type="html">Routour🗼: Building the Ultimate Travel Router</title><link href="https://archfx.me/posts/2025/02/router/" rel="alternate" type="text/html" title="Routour🗼: Building the Ultimate Travel Router" /><published>2025-02-23T00:00:00-08:00</published><updated>2025-02-23T00:00:00-08:00</updated><id>https://archfx.me/posts/2025/02/blog-post-26</id><content type="html" xml:base="https://archfx.me/posts/2025/02/router/"><![CDATA[<p>Recently I finished the project <strong>Routour🗼</strong>, a powerful travel router that is compact yet supports many features with components including Raspberry Pi Compute Module 4 (CM4), Intel BE200 WiFi card, DeskPi Mini Cube, and RaspAP with Debian and can be powered by a small power bank. In this post, I am documenting the entire process since I believe this would help someone in the future. This includes the hardware modifications and software configurations needed to achieve a feature-rich yet portable network router.</p>

<p><img src="/images/posts/routour_3.jpg" style="max-width:500px;width:100%;margin-right: 20px; display: block; margin-left: auto; margin-right: auto;" /></p>

<h2 id="features">Features</h2>

<p>Let’s start with the features first.</p>

<ul>
  <li>Full router functionalities with dual-band (2.4GHz and 5GHz) with gigabit ethernet</li>
  <li>WiFi repeater capabilities (WiFi Client mode and AP mode at once)</li>
  <li>Ability to host VPN, adblocking, and <a href="https://tailscale.com/">Tailscale</a></li>
  <li>Ability to run media servers and other containers with <a href="https://www.docker.com/">Docker</a></li>
  <li>Efficient on power consumption (less than 10W)</li>
  <li>Smallest possible size</li>
</ul>

<h2 id="hardware-part-of-the-story">Hardware Part of the Story</h2>

<p>For my build, I wanted the smallest form factor possible while still satisfying my requirements. So I opted to use standalone hardware with some modifications. I am outlining here the components I used for this project and the modifications I did for them, however, <strong>if you don’t want to do hardware modifications you can change the components based on your liking</strong>.</p>

<h3 id="components">Components</h3>
<ul>
  <li>Processing Unit : <a href="https://www.raspberrypi.com/products/compute-module-4/?variant=raspberry-pi-cm4001000">Raspberry Pi CM4</a> board without onboard wifi. Since the Raspberry Pi onboard wifi is not powerful enough, we will be using a separate wifi module. Therefore, opting for the CM4 without onboard wifi will save some power.</li>
  <li>Wifi Module : <a href="https://www.intel.com/content/www/us/en/products/sku/230078/intel-wifi-7-be200/specifications.html">Intel BE200NGW</a> dual band wifi card</li>
  <li>Case and Motherboard: <a href="https://deskpi.com/products/deskpi-mini-cube-for-raspberry-pi-compute-module-4-cm4">GeeekPi DeskPi Mini Cube</a> which has Gigabit ethernet and M.2 M-Key PCI-E interface.</li>
  <li>Adaptor Card: Since our Motherboard only has a M.2 M-Key Slot, we have to convert this to M.2 E-Key Slot so that we can use our wifi card.</li>
</ul>

<h3 id="modifications">Modifications</h3>

<p><img src="/images/posts/routour_2.jpg" style="max-width:250px;width:100%;margin-right: 10px;" align="left" />
For the CM4 I had a low-spec one lying around that I bought for a previous experiment which had 1GB RAM and 8GB EMMC. Since I wanted the Docker support, I upgraded the RAM and EMMC to 8GB and 128GB respectively (Yes, it is a tedious process that requires a lot of patience). GeeekPi Mini Cube is packed very much within a very tiny space. For this reason, the adaptor card and the wifi module do not fit within the available space without modifying M.2 M-Key Slot on the motherboard. I had to desolder the original M.2 connector, which was 8.5MM in height, to a connector with 6.5MM. Then I had to find a suitable pair of antennas with dual-band support. Note that BE200 antenna connections use MHF4 connectors, not UHF. Other than that, no major changes were required.</p>

<h2 id="softfirmware-part-of-the-story">Soft(Firm)ware Part of the Story</h2>

<p>I think this is the most important part of the building process for several reasons. I could not find good enough documentation to make everything work as I wanted. So after a lot of trial and error, I found the configurations that supported all the features that I wanted.</p>

<ul>
  <li><a href="https://wiki.debian.org/RaspberryPi4">Debian Bookworm</a> (64bit) without GUI</li>
  <li><a href="https://raspap.com/">RaspAP</a> is a powerful platform that supports all the fancy features that I wanted from my router.</li>
  <li>Intel BE200 firmware <a href="https://github.com/Archfx/rpi-be200">Driver</a>.</li>
</ul>

<p>I am not going to talk about installing each of the above components since there is information about installing each of the components in their corresponding documentation pages.</p>

<p><img src="/images/icons/raspap.png" style="max-width:120px;width:100%;margin-right: 10px;" align="left" /> By default, RaspAP supports wifi repeater mode (both in the community edition + insider edition) as long as you have two physical adaptors with two separate interfaces. But the interesting thing is that BE200 supports multiple bands and multiple channels. This means we should be able to create multiple interfaces in theory. This is where I had to do several experiments to figure out the components.</p>

<h3 id="virtual-wlan-interfaces">Virtual wlan Interfaces</h3>

<p>This process elaborates on the process of creating the virtual WLAN interface. I am giving the name <code class="language-plaintext highlighter-rouge">wlan0_ap</code> for this interface. You can create the interface with the following command.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>iw dev wlan0 interface add wlan0_ap <span class="nb">type </span>managed
</code></pre></div></div>

<p>You can check whether the new interface is available with the <code class="language-plaintext highlighter-rouge">ifconfig</code> command. Next, we have to set a static IP for the interface. You can use your favorite private network IP for this.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>ip addr add 10.4.141.1/24 dev wlan0_ap
<span class="nb">sudo </span>ip <span class="nb">link set </span>wlan0_ap up
</code></pre></div></div>
<p>You can verify the assignment of IP with the following command</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip addr show wlan0_ap
</code></pre></div></div>

<p>Next, we have to restart the socket related to the <code class="language-plaintext highlighter-rouge">iwlan0</code>.</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#reinitialize socket</span>
<span class="nb">sudo </span>wpa_supplicant <span class="nt">-B</span> <span class="nt">-Dnl80211</span>,wext <span class="nt">-c</span>/etc/wpa_supplicant/wpa_supplicant.conf <span class="nt">-iwlan0</span>
</code></pre></div></div>

<p>To make the interface available permanently, create a file <code class="language-plaintext highlighter-rouge">/etc/systemd/system/wifiap-setup.service</code> and place the following components inside the file.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">[</span>Unit]
<span class="nv">Description</span><span class="o">=</span>Setup wlan0_ap and wpa_supplicant on boot
<span class="nv">After</span><span class="o">=</span>network.target

<span class="o">[</span>Service]
<span class="nv">Type</span><span class="o">=</span>oneshot
<span class="nv">ExecStart</span><span class="o">=</span>/usr/sbin/iw dev wlan0 interface add wlan0_ap <span class="nb">type </span>managed
<span class="nv">ExecStart</span><span class="o">=</span>/usr/sbin/wpa_supplicant <span class="nt">-B</span> <span class="nt">-Dnl80211</span>,wext <span class="nt">-c</span>/etc/wpa_supplicant/wpa_supplicant.conf <span class="nt">-iwlan0</span>
<span class="nv">RemainAfterExit</span><span class="o">=</span><span class="nb">yes</span>

<span class="o">[</span>Install]
<span class="nv">WantedBy</span><span class="o">=</span>multi-user.target
</code></pre></div></div>
<p>Next, we have to reload systemd, enable, and start the service with the following commands.</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>systemctl daemon-reload
<span class="nb">sudo </span>systemctl <span class="nb">enable </span>wifiap-setup
<span class="nb">sudo </span>systemctl start wifiap-setup
</code></pre></div></div>

<h3 id="hotspot-and-configuring-dhcp">Hotspot and Configuring DHCP</h3>

<p>Once you have the interface up and running, you can use the newly created interface as the access point. To do that, login to the RaspAP admin panel and use the Hotspot tab. Select the newly created <code class="language-plaintext highlighter-rouge">wlan0_ap</code> interface. Configure the rest of the settings to your liking. After saving the settings and restarting the AP, you will see the SSID of your network. If you try to connect, it will fail due to an invalid (unavailable) DHCP.</p>

<p>Let’s configure the DHCP for the interface. Go to the DHCP server tab from the RaspAP admin panel. Remember the static IP (<code class="language-plaintext highlighter-rouge">10.4.141.1</code> in my case) used during the creation of the <code class="language-plaintext highlighter-rouge">wlan0_ap</code> interface. Based on that, we have to create the DHCP settings.</p>

<h2 id="conclusion">Conclusion</h2>

<p>At the end, <strong>Routour🗼</strong> has been a rewarding project. I hope this documentation helps others who are interested in building their own custom router. If you have any questions or suggestions, feel free to reach out.</p>]]></content><author><name>A·ru·nạ</name></author><category term="Network" /><category term="Security" /><category term="Router" /><category term="Raspap" /><summary type="html"><![CDATA[Recently I finished the project Routour🗼, a powerful travel router that is compact yet supports many features with components including Raspberry Pi Compute Module 4 (CM4), Intel BE200 WiFi card, DeskPi Mini Cube, and RaspAP with Debian and can be powered by a small power bank. In this post, I am documenting the entire process since I believe this would help someone in the future. This includes the hardware modifications and software configurations needed to achieve a feature-rich yet portable network router.]]></summary></entry><entry><title type="html">CrPT🔑: Lattice Problem</title><link href="https://archfx.me/posts/2025/01/lattice/" rel="alternate" type="text/html" title="CrPT🔑: Lattice Problem" /><published>2025-01-05T00:00:00-08:00</published><updated>2025-01-05T00:00:00-08:00</updated><id>https://archfx.me/posts/2025/01/blog-post-25</id><content type="html" xml:base="https://archfx.me/posts/2025/01/lattice/"><![CDATA[<p>Welcome to the second episode of the <strong>CrPT🔑</strong> series. In this episode, I discuss lattices and how they are connected to cryptography. As we discussed in the introduction episode, since quantum computers are expected to break many current asymmetric algorithms, lattice-based methods have attracted growing interest as a promising post-quantum alternative. Lattice-based cryptography relies on the hardness of solving lattice problems, let’s look at the fundamental idea behind them.</p>

<h2 id="what-is-a-lattice">What is a Lattice?</h2>

<p>A <strong>lattice</strong> in mathematics is a regular, repeating arrangement of points in an <em>n</em>-dimensional space. Formally, a lattice <strong>Λ</strong> in \(\mathbb{R}^n\) is defined as the set of all integer linear combinations of <em>n</em> linearly independent basis vectors <strong>v₁, v₂, …, vₙ</strong>:</p>

\[\Lambda = \left\{ a_1 \mathbf{v}_1 + a_2 \mathbf{v}_2 + \dots + a_n \mathbf{v}_n \;\bigg|\; a_i \in \mathbb{Z} \right\}\]

<p>Here, each vector \(\mathbf{v}_i\) serves as a <strong>basis vector</strong>, and the coefficients \(a_i\) are integers. This structure forms a discrete subgroup of \(\mathbb{R}^n\), providing the foundation for various applications in cryptography, such as constructing secure cryptographic schemes based on the hardness of lattice problems. The geometric properties of lattices, including their symmetry and densest packing arrangements, play a crucial role in their cryptographic strength and efficiency.</p>

<h2 id="lets-simplify-things">Let’s Simplify Things</h2>

<p>Instead of n-dimensional space, lets narrow down it to a two-dimensional space. Our basis vectors can be denoted as \(\mathbf{v}_1\) and \(\mathbf{v}_2\). So in this case, every point in this two-dimentional lattice can be expressed as an integer linear combination of these basis vectors:</p>

\[\Lambda = \left\{ a_1 \mathbf{v}_1 + a_2 \mathbf{v}_2 \;\bigg|\; a_1, a_2 \in \mathbb{Z} \right\}\]

<p>This means that by scaling and adding the basis vectors with integer coefficients, you can generate all the points that make up the lattice. The choice of basis vectors determines the shape and structure of the lattice.</p>

<div id="lattice-box" style="width:70%; aspect-ratio: 1 / 1; height:auto; margin: 20px auto; border: 1px solid #ccc;"></div>

<div class="input-container" style="text-align: center; margin: 10px auto; display: flex; align-items: center; justify-content: center; gap: 10px;">
  <label for="coeff-v1">a<sub>1</sub>:</label>
  <input type="number" id="coeff-v1" value="2" style="width: 50px;" />
  <label for="coeff-v2">a<sub>2</sub>:</label>
  <input type="number" id="coeff-v2" value="3" style="width: 50px;" />
  <button id="update-vector" style="height:28px; margin-bottom:7px; margin-left:10px">Update Result</button>
</div>

<script src="/assets/js/jsxgraph.js"></script>

<script>
  window.onload = function() {
    function setColor() { 
        const rgbValue = [
            Math.round(Math.random() * 255), 
            Math.round(Math.random() * 255), 
            Math.round(Math.random() * 255)
        ];

        const luminance = Math.round(
            (rgbValue[0] * 299 + rgbValue[1] * 587 + rgbValue[2] * 114) / 1000
        );
        const textColor = luminance > 125 ? 'white' : 'black';
        return textColor;
    }

    let textColor = setColor(); 

    var board = JXG.JSXGraph.initBoard('lattice-box', {
        axis: true,
        boundingbox: [-5, 5, 5, -5],
        grid: false,
        pan: true,
        zoom: true,
        defaultAxes: {
            x: {
                name: 'x',
                label: {
                    color: textColor 
                },
                ticks: {
                    label: { color: textColor } 
                }
            },
            y: {
                name: 'y',
                label: {
                    color: textColor 
                },
                ticks: {
                    label: { color: textColor } 
                }
            }
        }
    });

    var basisVector1 = board.create('point', [1, 0], { name: 'v1', size: 4, color: 'blue', label: { strokeColor: textColor, fontSize: 19 } });
    var basisVector2 = board.create('point', [0, 1], { name: 'v2', size: 4, color: 'green', label: { strokeColor: textColor, fontSize: 19 } });
    var origin = board.create('point', [0, 0], { name: 'O', fixed: true, size: 3, color: 'black', label: { strokeColor: textColor, fontSize: 15 } });

    var arrow1 = board.create('arrow', [origin, basisVector1], { strokeColor: 'blue', strokeWidth: 2 });
    var arrow2 = board.create('arrow', [origin, basisVector2], { strokeColor: 'green', strokeWidth: 2 });

    var latticePoints = [];
    function generateLattice() {
        latticePoints.forEach(point => board.removeObject(point));
        latticePoints = [];

        var x1 = basisVector1.X(), y1 = basisVector1.Y();
        var x2 = basisVector2.X(), y2 = basisVector2.Y();

        for (var i = -10; i <= 10; i++) {
            for (var j = -10; j <= 10; j++) {
                var px = i * x1 + j * x2;
                var py = i * y1 + j * y2;
                latticePoints.push(
                    board.create('point', [px, py], { size: 2, fixed: true, color: 'gray', name: '' })
                );
            }
        }
    }
    generateLattice();

    basisVector1.on('drag', function() {
        generateLattice();
        updateVector();
    });
    basisVector2.on('drag', function() {
        generateLattice();
        updateVector();
    });

    var linearComboPoint = board.create('point', [0, 0], {
        name: 'Result',
        size: 4,
        color: 'red',
        face: '^',
        label: {
            strokeColor: textColor,
            fontSize: 19 
        },
        layer: 10 
    });

    function updateVector() {
        const coeffV1 = parseFloat(document.getElementById('coeff-v1').value) || 0;
        const coeffV2 = parseFloat(document.getElementById('coeff-v2').value) || 0;

        const newX = coeffV1 * basisVector1.X() + coeffV2 * basisVector2.X();
        const newY = coeffV1 * basisVector1.Y() + coeffV2 * basisVector2.Y();

        linearComboPoint.setPosition(JXG.COORDS_BY_USER, [newX, newY]);
        board.update();
    }

    document.getElementById('update-vector').addEventListener('click', updateVector);

    updateVector();
  }
</script>

<p>In the interactive lattice visualization above, you can <strong>drag the blue and green basis vectors</strong> to different positions. As you move these vectors, the entire lattice adjusts in real-time to reflect the new basis. This dynamic behavior demonstrates how altering the basis vectors directly affects the arrangement and density of the lattice points.</p>

<p>Observe that any combination of basis vectors points to a point on the lattice it self. You can change the coffeicients fo the \(a_1\) and \(a_2\) to see how the result falls back again on the lattice. Try <strong>entering different coefficients</strong> in the input boxes and click the “Update Vector” button to observe how linear combinations of the basis vectors generate new lattice points.</p>

<h2 id="lattice-to-cryptography">Lattice to Cryptography</h2>

<p>Lattice-based cryptography relies on the hardness of lattice problems such as,</p>

<ul>
  <li>Shortest Vector Problem (SVP)</li>
  <li>Closest Vector Problem (CVP)</li>
</ul>

<p>which are believed to be resistant to both classical and quantum attacks. Additionally, lattice-based schemes often offer advanced functionalities like fully homomorphic encryption, which allows computations to be performed directly on encrypted data, and secure multi-party computation, enhancing their versatility and security in various applications.</p>

<h3 id="shortest-vector-problem-svp">Shortest Vector Problem (SVP)</h3>

<p>The <strong>Shortest Vector Problem (SVP)</strong> is a fundamental challenge in lattice theory and cryptography. Given a lattice <strong>Λ</strong> generated by a set of basis vectors, SVP seeks to find the shortest non-zero vector within the lattice. Formally, SVP can be defined as:</p>

\[\text{Find } \mathbf{v} \in \Lambda \setminus \{\mathbf{0}\} \text{ such that } \|\mathbf{v}\| = \min \{ \|\mathbf{w}\| : \mathbf{w} \in \Lambda \setminus \{\mathbf{0}\} \}\]

<h3 id="closest-vector-problem-cvp">Closest Vector Problem (CVP)</h3>

<p>The <strong>Closest Vector Problem (CVP)</strong> is another challenge in lattice theory and cryptography. Given a lattice <strong>Λ</strong> generated by a set of basis vectors and a target point <strong>t</strong> in \(\mathbb{R}^n\), CVP seeks to find the lattice vector <strong>v</strong> in <strong>Λ</strong> that is closest to <strong>t</strong> in terms of Euclidean distance. Formally, CVP can be defined as:</p>

\[\text{Find } \mathbf{v} \in \Lambda \text{ such that } \|\mathbf{v} - \mathbf{t}\| = \min \{ \|\mathbf{w} - \mathbf{t}\| : \mathbf{w} \in \Lambda \}\]

<p>SVP and CVP are widely recognized as computationally hard problems, especially in high-dimensional lattices. There are lattice-based cryptographic schemes that rely on the challenge of finding the closest lattice vector to a given target. In the subsequent episodes, we will explore why these problems are so challenging and how they are effectively utilized in cryptographic algorithms.</p>

<h2 id="conclusion">Conclusion</h2>

<p>In this episode of the <strong>CrPT🔑</strong> series, we explored the basics of lattices. You had the opportunity to select two basis vectors to create your own two-dimensional lattice using the interactive visualization tool. Additionally, we briefly discussed the Shortest Vector Problem (SVP) and Closest Vector Problem (CVP), which are foundational to lattice-based cryptography. In the upcoming episodes, we’ll look deeper into these problems and their applications in securing cryptographic systems.</p>]]></content><author><name>A·ru·nạ</name></author><category term="Platform" /><category term="Security" /><category term="Cryptography" /><summary type="html"><![CDATA[Welcome to the second episode of the CrPT🔑 series. In this episode, I discuss lattices and how they are connected to cryptography. As we discussed in the introduction episode, since quantum computers are expected to break many current asymmetric algorithms, lattice-based methods have attracted growing interest as a promising post-quantum alternative. Lattice-based cryptography relies on the hardness of solving lattice problems, let’s look at the fundamental idea behind them.]]></summary></entry><entry><title type="html">CrPT🔑: Applications of Cryptography</title><link href="https://archfx.me/posts/2025/01/crypto/" rel="alternate" type="text/html" title="CrPT🔑: Applications of Cryptography" /><published>2025-01-04T00:00:00-08:00</published><updated>2025-01-04T00:00:00-08:00</updated><id>https://archfx.me/posts/2025/01/blog-post-24</id><content type="html" xml:base="https://archfx.me/posts/2025/01/crypto/"><![CDATA[<p>Welcome to my new blog series <strong>CrPT🔑</strong> that discuss about applications of cryptography. In this first episode, I will explore current trends in cryptography and their impact on various applications. With significant developments in Artificial Intelligence (AI) and recent breakthroughs in quantum computing, it is a great time to discuss the future of cryptography. This episode aims to examine the factors that will and will not influence the future of applications that rely on cryptography.</p>

<p><img src="/images/posts/qc.jpg" /><br />
<small>AI Generated Image : credits to <a href="https://x.com/i/grok">grok2</a></small></p>

<h2 id="one-time-pad">One-Time Pad</h2>

<p>The one-time pad (OTP) is considered the only theoretically unbreakable cryptographic method when implemented correctly because it achieves perfect secrecy, meaning the ciphertext provides no information about the plaintext without the key, as proven by Claude Shannon. The key used in an OTP must be as long as the message, truly random, securely shared, and used only once.</p>

<p>To illustrate how a One-Time Pad (OTP) works, let’s consider a simple example involving two parties, Alice and Bob.</p>

<p><strong>Step 1: Key Generation</strong>
Alice and Bob agree on a random key that is as long as the message they wish to exchange. For this example, let’s use the message “HELLO”.</p>

<ul>
  <li><strong>Message:</strong> H E L L O</li>
  <li><strong>Key:</strong> X M C K L</li>
</ul>

<p><strong>Step 2: Encryption</strong>
Each character of the message is combined with the corresponding character of the key using a modular addition (for simplicity, we’ll use their ASCII values).</p>

<table>
  <thead>
    <tr>
      <th>Character</th>
      <th>H (72)</th>
      <th>E (69)</th>
      <th>L (76)</th>
      <th>L (76)</th>
      <th>O (79)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Key</td>
      <td>X (88)</td>
      <td>M (77)</td>
      <td>C (67)</td>
      <td>K (75)</td>
      <td>L (76)</td>
    </tr>
    <tr>
      <td>Encrypted</td>
      <td>(72+88) % 256 = 160</td>
      <td>(69+77) % 256 = 146</td>
      <td>(76+67) % 256 = 143</td>
      <td>(76+75) % 256 = 151</td>
      <td>(79+76) % 256 = 155</td>
    </tr>
  </tbody>
</table>

<ul>
  <li><strong>Ciphertext:</strong> 160 146 143 151 155</li>
</ul>

<p><strong>Step 3: Decryption</strong>
Bob, who possesses the same key, can decrypt the ciphertext by subtracting the key values from the ciphertext.</p>

<table>
  <thead>
    <tr>
      <th>Ciphertext</th>
      <th>160</th>
      <th>146</th>
      <th>143</th>
      <th>151</th>
      <th>155</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Key</td>
      <td>88</td>
      <td>77</td>
      <td>67</td>
      <td>75</td>
      <td>76</td>
    </tr>
    <tr>
      <td>Message</td>
      <td>(160-88) = 72 (H)</td>
      <td>(146-77) = 69 (E)</td>
      <td>(143-67) = 76 (L)</td>
      <td>(151-75) = 76 (L)</td>
      <td>(155-76) = 79 (O)</td>
    </tr>
  </tbody>
</table>

<ul>
  <li><strong>Decrypted Message:</strong> H E L L O</li>
</ul>

<p><strong>Requirements of OTP:</strong></p>
<ul>
  <li>The key must be <strong>truly random</strong>, <strong>same size as the message</strong> and <strong>kept secret</strong>.</li>
  <li>The <strong>key is used only once</strong>; reusing keys compromises security.</li>
  <li>The security of OTP relies on the randomness and secrecy of the key.</li>
</ul>

<p>However, if you carefully consider the first two requirements, you’ll notice a significant drawback of the OTP. The key must be as long as your message, which defeats its original purpose because transferring the key securely requires the same effort as transferring the message itself. Almost all cryptographic algorithms used today address this issue in various ways, such as through the use of asymmetric encryption, which allows secure key exchange without the need for pre-shared keys, or by employing key derivation functions and protocols like Diffie-Hellman to enable secure communication over insecure channels. Additionally, these algorithms utilize shorter keys by using complex mathematical problems and computational hardness assumptions. They help to implement robust security without the need for excessively long keys.</p>

<h2 id="artificial-intelligence-ai-and-cryptography">Artificial Intelligence (AI) and Cryptography?</h2>

<p>Now that we understand the basics of the One-Time Pad, let’s examine whether AI will be able to take over modern cryptography. As mentioned earlier, cryptographic algorithms are designed based on hardness assumptions. For example, brute-forcing a message encrypted using AES-CTR-256 would take billions of years even for a supercomputer. The same applies to training an AI model to crack modern encryption algorithms. Let me explain why…</p>

<h3 id="cryptography-removes-patterns-in-data">Cryptography removes Patterns in Data</h3>

<p>Generative models, such as those in machine learning, cannot replicate the security of an OTP because they produce pseudo-random sequences rather than truly random ones, and they are designed to recognize and replicate patterns, which contradicts the OTP’s requirement for patternless keys. In otherwords, AI models are pre-trained on data, such that they can make a prediction on new data by understading the pattern of the data. However, the major controbution of cryptograohy is to remove the patterns from the data.</p>

<p>We can replicate this analogy with a simple illustration of a hash function. In the text box below we have a text string, the correspoding SHA-512 string is on the next message box. Try changing the value of the text string and observe how the hash value of the string changes with that.</p>

<div style="text-align: center; margin-bottom: 20px; border: 1px solid black; padding: 10px;">
  
<div style="text-align: center; margin-bottom: 20px;">
  <div>
    <label for="inputText">Text String:</label>
    <input type="text" id="inputText" value="Hello World !!" oninput="computeHash()" />
  </div>
</div>
<div style="text-align: center;">
  <label for="hashOutput">SHA-512 Hash:</label>
  <span id="hashOutput" style="word-break: break-all;">785f9615b02989575a0d28aa071f3bdb78f1729d091823449631a5430b719aaf413fa5c949181d7ee1e3c732430a74fec6d5a464a1adbc87201aad6b27d96bc8</span>
</div>
</div>

<script>
  async function computeHash() {
    const text = document.getElementById('inputText').value;
    const encoder = new TextEncoder();
    const data = encoder.encode(text);
    const hashBuffer = await crypto.subtle.digest('SHA-512', data);
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
    document.getElementById('hashOutput').textContent = hashHex;
  }
</script>

<p>As you oubserved, it a machine learning model needs to learn this using traning data, essentially it needs to learn all the patterns. At this point it becomes a table lookup rather than a prediction.</p>

<h3 id="secret-keys-add-more-complexity">Secret Keys Add More Complexity</h3>

<p>Making this relation more complex, the most common encryption, decryption, sign and verify mechanism uses various forms of secret keys. Unless the key is compromised, finding a non-existing pattern between plain text and cypher text has too much of complexity.</p>

<p>To understand this, lets look at the following illustration.</p>

<div style="text-align: center; margin-bottom: 20px; border: 1px solid black; padding: 10px;">
  
<div style="text-align: center; margin-bottom: 20px;">
  <div>
    <label for="aesKey">Encryption Key:</label>
    <input type="text" id="aesKey" value="0123456789abcdef0123456789abcdef" oninput="computeAES()" />
  </div>
</div>
<div style="text-align: center; margin-bottom: 20px;">
  <div>
    <label for="aesInput">AES-CTR Input:</label>
    <input type="text" id="aesInput" value="Hello World!!" oninput="computeAES()" />
  </div>
</div>
<div style="text-align: center;">
  <label for="aesOutput">AES-CTR Encrypted:</label>
  <span id="aesOutput">cd6b574d69b4a19897b68e6229</span>
</div>
</div>

<script>
  async function computeAES() {
    const text = document.getElementById('aesInput').value;
    const keyInput = document.getElementById('aesKey').value;
    const encoder = new TextEncoder();
    const data = encoder.encode(text);
    const keyMaterial = encoder.encode(keyInput); 
    const key = await crypto.subtle.importKey(
      'raw',
      keyMaterial,
      { name: 'AES-CTR' },
      false,
      ['encrypt']
    );
    const iv = crypto.getRandomValues(new Uint8Array(16)); 
    const encrypted = await crypto.subtle.encrypt(
      { name: 'AES-CTR', counter: iv, length: 64 },
      key,
      data
    );
    const encryptedArray = Array.from(new Uint8Array(encrypted));
    const encryptedHex = encryptedArray.map(b => b.toString(16).padStart(2, '0')).join('');
    document.getElementById('aesOutput').textContent = encryptedHex;
  }
</script>

<p>As you have seen in the above illustrations, AI is not in a position to impact modern cryptography. Someone might argue about what would happen if computing power increases to the point where AI can utilize vast resources. The answer is simple: in such situations, AI is not required, and brute force attacks can be conducted with the same effort. However, cryptographic algorithm parameters, such as key sizes, can be increased to account for potential computational power. An example of this is the deprecation of DES in favor of more secure algorithms like AES, due to the potential for brute-forcing with modern computers.</p>

<h2 id="quantum-computers-and-cryptography">Quantum Computers and Cryptography?</h2>

<p>Although we don’t have to worry about AI breaking cryptography, quantum computers have the potential to become a threat to some of the algorithms used to build the public key infrastructure (PKI). Cryptographic algorithms like RSA and Elliptic Curve-based Cryptography (ECC) are implemented based on assumptions about the hardness of prime factorization. Theoretically, with a quantum computer with enough qubits, <a href="https://en.wikipedia.org/wiki/Shor%27s_algorithm">Shor’s algorithm</a> can solve the prime factorization problem.</p>

<h3 id="when-quantum-computers-will-take-over">When Quantum Computers Will Take Over?</h3>

<p>So the next question arises: What happens to the current infrastructure that uses PKI, such as blockchain, network authentication, and secure communication systems? We don’t need to panic about this question at the moment. Current quantum computers do not have the capacity to break modern PKI yet. Even Google’s latest quantum computer, <a href="https://blog.google/technology/research/google-willow-quantum-chip/">Willow</a>, has 105 qubits, which is not capable of breaking RSA-1024, a standard that is already deprecated due to its low security strength. Quantum computers need to successfully solve the issue of “noise” in order to scale well enough to achieve this capability.</p>

<h3 id="post-quantum-cryptography">Post Quantum Cryptography</h3>

<p>Although current quantum computers are not powerful enough yet, advancements in science may allow them to scale to a level capable of breaking PKI. As a precaution, researchers are proposing post-quantum cryptographic algorithms. The <a href="https://www.nist.gov/">National Institute of Standards and Technology (NIST)</a> has standardize post-quantum cryptographic algorithms. Aiming to deprecate vulnerable PKI algorithms like RSA and Elliptic Curve Cryptography (ECC) by 2030, NIST is evaluating and selecting new standards that can withstand quantum attacks. The selected algorithms include CRYSTALS-Kyber for key encapsulation and CRYSTALS-Dilithium for digital signatures.</p>

<p>The concepts of these algorithms are based on two main problems:</p>

<ol>
  <li>Lattice Problems</li>
  <li>Learning with Errors</li>
</ol>

<p>In the rest of this series, we will discuss these techniques with simplified examples and illustrations.</p>

<h2 id="conclusion">Conclusion</h2>

<p>In this first episode of <strong>CrPT🔑</strong>, we have explored the fundamental principles of cryptography, the potential threats posed by advancements in artificial intelligence and quantum computing, and the proactive measures being taken to secure our digital future. While AI may not currently pose a significant threat to modern cryptographic systems, the rise of quantum computing necessitates the development of robust post-quantum algorithms. So in the subsequant articles, lets discuss more about the concepts behind these new algorithms, so that we can be familier with them before it is too late.</p>]]></content><author><name>A·ru·nạ</name></author><category term="Platform" /><category term="Security" /><category term="Cryptography" /><category term="Applications" /><summary type="html"><![CDATA[Welcome to my new blog series CrPT🔑 that discuss about applications of cryptography. In this first episode, I will explore current trends in cryptography and their impact on various applications. With significant developments in Artificial Intelligence (AI) and recent breakthroughs in quantum computing, it is a great time to discuss the future of cryptography. This episode aims to examine the factors that will and will not influence the future of applications that rely on cryptography.]]></summary></entry><entry><title type="html">Android Device Security and GSI: A Layman’s Guide</title><link href="https://archfx.me/posts/2024/12/androidsecurity/" rel="alternate" type="text/html" title="Android Device Security and GSI: A Layman’s Guide" /><published>2024-12-10T00:00:00-08:00</published><updated>2024-12-10T00:00:00-08:00</updated><id>https://archfx.me/posts/2024/12/blog-post-23</id><content type="html" xml:base="https://archfx.me/posts/2024/12/androidsecurity/"><![CDATA[<p>Android devices come with several security components that work together to protect your device and personal data. Here’s a simple explanation of who makes these components, their security implications, and how they collaborate to keep your device (data) safe.</p>

<p><img src="/images/posts/android-sec.png" /><br />
<small>AI Generated Image : credits to <a href="https://chatgpt.com/">chatGPT</a></small></p>

<h2 id="key-components-and-entities">Key Components and Entities</h2>
<p>When it comes to an Android device—whether it’s a mobile phone, tablet, car entertainment system, or any other form factor—there are several key components involved in its realization:</p>

<ol>
  <li>
    <p><strong>Bootloader</strong>: The initial software that runs when the device powers on, responsible for starting the kernel.</p>
  </li>
  <li>
    <p><strong>Linux kernel</strong>, serves as the core subsystem of the Android operating system, managing communication between hardware and software.</p>
  </li>
  <li>
    <p><strong>Android system image</strong>, includes the Android operating system and essential applications. Android is open source, vendors can modify the implementation to suit their own hardware as they wish.</p>
  </li>
</ol>

<p>Each of these components is developed by different entities: the bootloader is provided by the hardware manufacturer (<strong>vendor</strong>), the Linux kernel is developed by the open-source community under the <a href="https://github.com/torvalds/linux"><strong>Linux</strong></a> Foundation, and the Android system image is provided by <a href="https://about.google/"><strong>Google</strong></a>. Device manufacturers can modify the Android system image to suit their own hardware as they wish.</p>

<h3 id="the-chain-of-trust-among-components">The Chain of Trust Among Components</h3>

<p>The bootloader, Linux kernel, and Android system image work together to create a secure environment for your device. When the device powers on, the bootloader verifies the kernel’s integrity using cryptographic signatures. Subsequently, the kernel verifies the Android system image before loading it. This sequential verification establishes a chain of trust, ensuring that each component is authentic and has not been tampered with. Additionally, Over-The-Air (OTA) updates are signed with private keys and verified using public keys, maintaining the security and integrity of the updates applied to the system image.</p>

<h2 id="generic-system-images-gsi">Generic System Images (GSI)</h2>

<p>When manufacturers stop providing updates for their devices, users can turn to Generic System Images (GSIs) to receive the latest Android versions. GSIs are system images with adjusted configurations for Android devices, compatible with Project Treble.</p>

<p><strong>What Is a GSI?</strong> A GSI is a barebones Android OS image that can run on any device compliant with Project Treble, regardless of the manufacturer.</p>

<p><strong>Who Provides It?</strong> While Google provides the official Android source code via <a href="https://source.android.com/">Android Open Source Project (AOSP)</a>, it is utilized by several open-source projects to run the latest Android versions on devices abandoned by their vendors.  There are more generic GSI versions, such as <a href="https://github.com/TrebleDroid"><strong>TrebleDroid</strong></a> and <a href="https://github.com/ponces/treble_aosp"><strong>treble_aosp</strong></a>, which work on many different devices, and device-specific GSI versions, like <a href="https://archfx.me/duo-de/"><strong>DUO-DE</strong></a>, designed specifically for the Microsoft <a href="https://www.microsoft.com/en-us/d/surface-duo-2/9408kgxp4xjl?activetab=pivot:phonecallstab">Surface Duo</a>’s unique dual-screen foldable configuration. Often, people confuse these GSI images with Pixel ROMs; however, GSIs are implemented using the barebone Android images provided by Google.</p>

<h3 id="gsi-and-security">GSI and Security</h3>

<p>To install a GSI image on an Android device, you must unlock the device’s bootloader unless you are the vendor with access to the device’s private keys. Unlocking the bootloader disables certain trust mechanisms implemented in the bootloader. However, security features such as <a href="https://developer.android.com/tools/adb">Android Debug Bridge (ADB)</a> authorization continue to protect your device data if the device is lost or falls into the possession of an adversary.</p>

<p>Since unlocking the bootloader disables the security mechanisms it provides, the trust in the GSI is established independently. To ensure this trust, GSI maintainers use their own release keys to sign the GSI packages. These keys are similar to vendor keys. They provide the same level of security for operations on the device, preventing unauthorized modifications and maintaining the integrity of the system. Once you manually flash a GSI image to your device, GSI keys ensure the security of the following components.</p>

<ol>
  <li>
    <p>They ensure that over-the-air (OTA) updates are authentic and have not been tampered with. During installation, the device verifies the signature using the embedded public key.</p>
  </li>
  <li>
    <p>Protection of System Applications: Without the proper release keys, system applications cannot be replaced or modified.</p>
  </li>
</ol>

<p>Since the Android system of the GSI is originally from the Android, all the security features available in the lastet version of the Android is availble to the users via GSI.</p>

<h2 id="conclusion">Conclusion</h2>

<p>In this guide, we’ve explored the key components that ensure the security of Android devices, including the <strong>bootloader</strong>, <strong>Linux kernel</strong>, and <strong>Android system image</strong>. We discussed how these components interact through a <strong>chain of trust</strong>, utilizing cryptographic signatures with private and public keys to verify each stage of the boot process and Over-The-Air (OTA) updates. Additionally, we discussed about <strong>Generic System Images (GSIs)</strong>, highlighting their role in providing up-to-date Android versions for devices no longer receiving manufacturer updates.</p>

<p>Ultimately, the trustworthiness of an Android device hinges on the reliability of its manufacturers and, in the case of GSIs, the maintainers behind these open-source projects. When using a device from an unknown manufacturer, users must place a certain level of trust in that manufacturer’s commitment to security. Similarly, opting for a GSI replaces the manufacturer’s role with GSI maintainers. The advantage here is that most GSI projects are open source, allowing the public to review and contribute to their implementations, thereby enhancing transparency and security.</p>

<p><strong>Key Takeaways:</strong></p>

<ul>
  <li><strong>Chain of Trust:</strong> Ensures that each component of the Android system is verified and secure, preventing unauthorized modifications.</li>
  <li><strong>GSIs as a Solution:</strong> Provide a way to keep devices secure and updated even after manufacturers cease support.</li>
  <li><strong>Open Source Benefits:</strong> GSIs benefit from community oversight, increasing the likelihood of identifying and addressing security vulnerabilities promptly.</li>
  <li><strong>Trust Considerations:</strong> Whether relying on manufacturers or GSI maintainers, users must trust that the entities managing their device’s software prioritize security and integrity.</li>
</ul>

<p>By understanding these components and the dynamics of trust in both manufacturer-provided and community-driven updates, users can make informed decisions to maintain the security and longevity of their Android devices.</p>]]></content><author><name>A·ru·nạ</name></author><category term="Firmware" /><category term="OS" /><category term="Android" /><category term="GSI" /><summary type="html"><![CDATA[Android devices come with several security components that work together to protect your device and personal data. Here’s a simple explanation of who makes these components, their security implications, and how they collaborate to keep your device (data) safe.]]></summary></entry><entry><title type="html">TinyOS🐞: Interrupts and Peripherals</title><link href="https://archfx.me/posts/2024/04/tinyos7/" rel="alternate" type="text/html" title="TinyOS🐞: Interrupts and Peripherals" /><published>2024-04-08T00:00:00-07:00</published><updated>2024-04-08T00:00:00-07:00</updated><id>https://archfx.me/posts/2024/04/blog-post-22</id><content type="html" xml:base="https://archfx.me/posts/2024/04/tinyos7/"><![CDATA[<p>A computer needs to communicate with the external world to perform tasks. To facilitate this, we have peripheral hardware. When these peripherals need to talk to the operating system, we have interrupts. In this episode of <strong>TinyOS</strong>🐞 tutorial series, we will be looking at interrupts and how to use them.</p>

<h3 id="programmable-interrupt-controller">Programmable Interrupt Controller</h3>

<p>A Programmable Interrupt Controller (PIC) is a hardware component in the system that is crucial for managing interrupt requests (IRQs) from various peripherals. Its primary function is to prioritize interrupt signals from hardware peripherals based on their urgency and importance, making sure that critical tasks are handled promptly. When an interrupt occurs, the PIC suspends the CPU’s current task and directs it to an interrupt handler, which manages the interrupt and executes necessary actions. The PIC also allows for interrupt masking and priority configuration, enabling system designers to customize interrupt handling according to specific requirements. While modern computer systems may utilize more advanced interrupt controllers like the Advanced Programmable Interrupt Controller (APIC), the fundamental role of prioritizing and managing interrupts remains essential for efficient system operation.</p>

<h3 id="interrupt">Interrupt</h3>

<p>Let’s first review the types of interrupts in RISC-V, which can be broken down into several major categories:</p>

<ul>
  <li>Local Interrupt
    <ul>
      <li>Software Interrupt</li>
      <li>Timer Interrupt</li>
    </ul>
  </li>
  <li>Global Interrupt
    <ul>
      <li>External Interrupt</li>
    </ul>
  </li>
</ul>

<p>The Exception Code of various interrupts is also defined in detail in the RISC-V specification</p>

<blockquote>
  <p>Specifically, the exception code will be recorded in the mcause register.</p>
</blockquote>

<p>If we want system programs running in RISC-V to support interrupt processing, we also need to set the field value of the MIE Register:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Machine-mode Interrupt Enable</span>
<span class="cp">#define MIE_MEIE (1 &lt;&lt; 11) // external
#define MIE_MTIE (1 &lt;&lt; 7)  // timer
#define MIE_MSIE (1 &lt;&lt; 3)  // software
</span><span class="c1">// enable machine-mode timer interrupts.</span>
<span class="n">w_mie</span><span class="p">(</span><span class="n">r_mie</span><span class="p">()</span> <span class="o">|</span> <span class="n">MIE_MTIE</span><span class="p">);</span>
</code></pre></div></div>

<h3 id="pic-in-risc-v">PIC in RISC-V</h3>

<p>RISC-V has its own Programmable Interrupt Controller implementation known as Platform-Level Interrupt Controller (PLIC). As we discussed earlier, there can be multiple interrupt sources (keyboard, mouse, hard disk…) connected to PLIC of a system. PLIC will determine the priority of these interrupts and then allocate them to the processor’s Hart (the minimum hardware thread in RISC-V) for processing by the CPU.</p>

<h3 id="interrupt-request">Interrupt Request</h3>

<p>An Interrupt Request is also known as IRQ, is a mechanism used by hardware devices to signal the CPU that they need attention or service. When a hardware device requires the CPU to perform a task, such as processing incoming data or handling an event, it sends an interrupt request. The CPU then temporarily suspends its current operation, saves its state, and jumps to a predefined location in memory known as an interrupt handler. This handler executes the necessary actions to address the request from the device. IRQs are assigned unique numerical identifiers, typically ranging from 0 to 15 in legacy systems, to distinguish between different interrupt sources. Each IRQ is associated with specific hardware components, such as keyboards, mice, storage devices, or network cards, allowing the CPU to prioritize and handle interrupts appropriately.
Taking the RISC-V virtual machine - Virt in Qemu as an example, its <a href="https://github.com/qemu/qemu/blob/master/include/hw/riscv/virt.h">source code</a> defines IRQs for different interrupts as follows:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">enum</span> <span class="p">{</span>
    <span class="n">UART0_IRQ</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span>
    <span class="n">RTC_IRQ</span> <span class="o">=</span> <span class="mi">11</span><span class="p">,</span>
    <span class="n">VIRTIO_IRQ</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="cm">/* 1 to 8 */</span>
    <span class="n">VIRTIO_COUNT</span> <span class="o">=</span> <span class="mi">8</span><span class="p">,</span>
    <span class="n">PCIE_IRQ</span> <span class="o">=</span> <span class="mh">0x20</span><span class="p">,</span> <span class="cm">/* 32 to 35 */</span>
    <span class="n">VIRTIO_NDEV</span> <span class="o">=</span> <span class="mh">0x35</span> <span class="cm">/* Arbitrary maximum number of interrupts */</span>
<span class="p">};</span>
</code></pre></div></div>

<p>When we are writing an operating system, we can use the IRQ code to identify the type of external interrupt and solve the problems of keyboard input and disk reading and writing.</p>

<h3 id="configuring-the-pic">Configuring the PIC</h3>

<p>As the name of PIC suggests, it can be programmed. For this purpose, PLIC adopts a Memory Map mechanism, which maps some important information to Main Memory. In this way, we can communicate with PLIC by accessing the memory. We can find these memory map definitions in <a href="https://github.com/qemu/qemu/blob/master/hw/riscv/virt.c">Virt’s source code</a>, which defines the virtual locations of PLIC as follows,</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="k">const</span> <span class="n">MemMapEntry</span> <span class="n">virt_memmap</span><span class="p">[]</span> <span class="o">=</span> <span class="p">{</span>
    <span class="p">[</span><span class="n">VIRT_DEBUG</span><span class="p">]</span> <span class="o">=</span>       <span class="p">{</span>        <span class="mh">0x0</span><span class="p">,</span>         <span class="mh">0x100</span> <span class="p">},</span>
    <span class="p">[</span><span class="n">VIRT_MROM</span><span class="p">]</span> <span class="o">=</span>        <span class="p">{</span>     <span class="mh">0x1000</span><span class="p">,</span>        <span class="mh">0xf000</span> <span class="p">},</span>
    <span class="p">[</span><span class="n">VIRT_TEST</span><span class="p">]</span> <span class="o">=</span>        <span class="p">{</span>   <span class="mh">0x100000</span><span class="p">,</span>        <span class="mh">0x1000</span> <span class="p">},</span>
    <span class="p">[</span><span class="n">VIRT_RTC</span><span class="p">]</span> <span class="o">=</span>         <span class="p">{</span>   <span class="mh">0x101000</span><span class="p">,</span>        <span class="mh">0x1000</span> <span class="p">},</span>
    <span class="p">[</span><span class="n">VIRT_CLINT</span><span class="p">]</span> <span class="o">=</span>       <span class="p">{</span>  <span class="mh">0x2000000</span><span class="p">,</span>       <span class="mh">0x10000</span> <span class="p">},</span>
    <span class="p">[</span><span class="n">VIRT_PCIE_PIO</span><span class="p">]</span> <span class="o">=</span>    <span class="p">{</span>  <span class="mh">0x3000000</span><span class="p">,</span>       <span class="mh">0x10000</span> <span class="p">},</span>
    <span class="p">[</span><span class="n">VIRT_PLIC</span><span class="p">]</span> <span class="o">=</span>        <span class="p">{</span>  <span class="mh">0xc000000</span><span class="p">,</span> <span class="n">VIRT_PLIC_SIZE</span><span class="p">(</span><span class="n">VIRT_CPUS_MAX</span> <span class="o">*</span> <span class="mi">2</span><span class="p">)</span> <span class="p">},</span>
    <span class="p">[</span><span class="n">VIRT_UART0</span><span class="p">]</span> <span class="o">=</span>       <span class="p">{</span> <span class="mh">0x10000000</span><span class="p">,</span>         <span class="mh">0x100</span> <span class="p">},</span>
    <span class="p">[</span><span class="n">VIRT_VIRTIO</span><span class="p">]</span> <span class="o">=</span>      <span class="p">{</span> <span class="mh">0x10001000</span><span class="p">,</span>        <span class="mh">0x1000</span> <span class="p">},</span>
    <span class="p">[</span><span class="n">VIRT_FW_CFG</span><span class="p">]</span> <span class="o">=</span>      <span class="p">{</span> <span class="mh">0x10100000</span><span class="p">,</span>          <span class="mh">0x18</span> <span class="p">},</span>
    <span class="p">[</span><span class="n">VIRT_FLASH</span><span class="p">]</span> <span class="o">=</span>       <span class="p">{</span> <span class="mh">0x20000000</span><span class="p">,</span>     <span class="mh">0x4000000</span> <span class="p">},</span>
    <span class="p">[</span><span class="n">VIRT_PCIE_ECAM</span><span class="p">]</span> <span class="o">=</span>   <span class="p">{</span> <span class="mh">0x30000000</span><span class="p">,</span>    <span class="mh">0x10000000</span> <span class="p">},</span>
    <span class="p">[</span><span class="n">VIRT_PCIE_MMIO</span><span class="p">]</span> <span class="o">=</span>   <span class="p">{</span> <span class="mh">0x40000000</span><span class="p">,</span>    <span class="mh">0x40000000</span> <span class="p">},</span>
    <span class="p">[</span><span class="n">VIRT_DRAM</span><span class="p">]</span> <span class="o">=</span>        <span class="p">{</span> <span class="mh">0x80000000</span><span class="p">,</span>           <span class="mh">0x0</span> <span class="p">},</span>
<span class="p">};</span>
</code></pre></div></div>
<p>Each PIC interrupt source will be represented by a temporary register. By adding <code class="language-plaintext highlighter-rouge">PLIC_BASE</code> to the offset <code class="language-plaintext highlighter-rouge">offset</code> of the temporary register, we can know the location where the temporary register is mapped to the main memory.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0xc000000 <span class="o">(</span>PLIC_BASE<span class="o">)</span> + offset <span class="o">=</span> Mapped Address of register
</code></pre></div></div>

<h2 id="interrupts-to-tinyos">Interrupts to TinyOS</h2>

<p>I think so far we looked at the background of the interrupts. Let’s add this functionality to the TinyOS operating system. First, we need to initialize Virt’s PLIC controller. For that, we use the <code class="language-plaintext highlighter-rouge">plic_init()</code> function, which is defined in <a href="https://github.com/Archfx/tinyos/blob/master/07-ExterInterrupt/plic.c">plic.c</a>:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">plic_init</span><span class="p">()</span>
<span class="p">{</span>
  <span class="kt">int</span> <span class="n">hart</span> <span class="o">=</span> <span class="n">r_tp</span><span class="p">();</span>
  <span class="c1">// QEMU Virt machine support 7 priority (1 - 7),</span>
  <span class="c1">// The "0" is reserved, and the lowest priority is "1".</span>
  <span class="o">*</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="o">*</span><span class="p">)</span><span class="n">PLIC_PRIORITY</span><span class="p">(</span><span class="n">UART0_IRQ</span><span class="p">)</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>

  <span class="cm">/* Enable UART0 */</span>
  <span class="o">*</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="o">*</span><span class="p">)</span><span class="n">PLIC_MENABLE</span><span class="p">(</span><span class="n">hart</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="n">UART0_IRQ</span><span class="p">);</span>

  <span class="cm">/* Set priority threshold for UART0. */</span>

  <span class="o">*</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="o">*</span><span class="p">)</span><span class="n">PLIC_MTHRESHOLD</span><span class="p">(</span><span class="n">hart</span><span class="p">)</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="cm">/* enable machine-mode external interrupts. */</span>
  <span class="n">w_mie</span><span class="p">(</span><span class="n">r_mie</span><span class="p">()</span> <span class="o">|</span> <span class="n">MIE_MEIE</span><span class="p">);</span>

  <span class="c1">// enable machine-mode interrupts.</span>
  <span class="n">w_mstatus</span><span class="p">(</span><span class="n">r_mstatus</span><span class="p">()</span> <span class="o">|</span> <span class="n">MSTATUS_MIE</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>As shown in the above example, <code class="language-plaintext highlighter-rouge">plic_init()</code> mainly performs following initialization actions:</p>

<ul>
  <li>Set the priority of UART_IRQ. Since PLIC can manage multiple external interrupt sources, we must set priorities for different interrupt sources. Then in case of conflicting requests, PLIC will know which IRQ to process first.</li>
  <li>Enable UART interrupt for hart0</li>
  <li>Set threshold. IRQs less than or equal to this threshold will be ignored by PLIC. We can configure the threshold using,
    <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">*</span><span class="p">(</span><span class="kt">uint32_t</span> <span class="o">*</span><span class="p">)</span><span class="n">PLIC_MTHRESHOLD</span><span class="p">(</span><span class="n">hart</span><span class="p">)</span> <span class="o">=</span> <span class="mi">10</span><span class="p">;</span>
</code></pre></div>    </div>
    <p>In this way, the system will not process the UART’s IRQ.</p>
  </li>
  <li>Enable external interrupts and global interrupts in Machine mode. It should be noted that this project originally used <code class="language-plaintext highlighter-rouge">trap_init()</code> to enable global interrupts in Machine mode. After this modification, we changed <code class="language-plaintext highlighter-rouge">plic_init()</code> to be responsible.</li>
</ul>

<p>Note that the peripherals also need configuration. In the case of UART, settings such as <strong>baud rate</strong> and other actions. <code class="language-plaintext highlighter-rouge">uart_init()</code> is defined in <a href="https://github.com/Archfx/tinyos/blob/master/07-ExterInterrupt/lib.c">lib.c</a>.</p>

<h3 id="modify-trap-handler">Modify Trap Handler</h3>

<p>We discussed about trap hander in the episode <a href="https://archfx.github.io/posts/2024/04/tinyos5/">Preemptive Scheduling</a>. You might remember the following diagram.</p>

<pre class="mermaid">
graph LR
    C[trap_handler] --&gt; D[soft_handler]
    C --&gt; E[timer_handler]
    C --&gt; F[exter_handler]

</pre>

<p>Previously in <a href="https://archfx.github.io/posts/2024/04/tinyos5/">Preemptive Scheduling</a>, <code class="language-plaintext highlighter-rouge">trap_handler()</code> only supported the processing of time interrupts. This time we want to make it support the processing of external interrupts as well.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* In trap.c */</span>
<span class="kt">void</span> <span class="nf">external_handler</span><span class="p">()</span>
<span class="p">{</span>
  <span class="kt">int</span> <span class="n">irq</span> <span class="o">=</span> <span class="n">plic_claim</span><span class="p">();</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">irq</span> <span class="o">==</span> <span class="n">UART0_IRQ</span><span class="p">)</span>
  <span class="p">{</span>
    <span class="n">lib_isr</span><span class="p">();</span>
  <span class="p">}</span>
  <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">irq</span><span class="p">)</span>
  <span class="p">{</span>
    <span class="n">lib_printf</span><span class="p">(</span><span class="s">"unexpected interrupt irq = %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">irq</span><span class="p">);</span>
  <span class="p">}</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">irq</span><span class="p">)</span>
  <span class="p">{</span>
    <span class="n">plic_complete</span><span class="p">(</span><span class="n">irq</span><span class="p">);</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Because the goal this time is to enable the operating system to process UART IRQ, we need to add that to the interrupt request as above. This will invoke the function <code class="language-plaintext highlighter-rouge">lib_isr()</code>.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* In lib.c */</span>
<span class="kt">void</span> <span class="nf">lib_isr</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">for</span> <span class="p">(;;)</span>
    <span class="p">{</span>
        <span class="kt">int</span> <span class="n">c</span> <span class="o">=</span> <span class="n">lib_getc</span><span class="p">();</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">c</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">break</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">else</span>
        <span class="p">{</span>
            <span class="n">lib_putc</span><span class="p">((</span><span class="kt">char</span><span class="p">)</span><span class="n">c</span><span class="p">);</span>
            <span class="n">lib_putc</span><span class="p">(</span><span class="sc">'\n'</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The principle of <code class="language-plaintext highlighter-rouge">lib_isr()</code> is quite simple. It just repeatedly detects whether the UART’s RHR register has received new data. If it is empty (c == -1), it jumps out of the loop. Registers related to UART are defined in <a href="https://github.com/Archfx/tinyos/blob/master/07-ExterInterrupt/riscv.h">riscv.h</a>. Some register addresses have been added to support <code class="language-plaintext highlighter-rouge">lib_getc()</code>. The general definitions of UART registers are as follows:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="cp">#define UART 0x10000000L
</span> <span class="cp">#define UART_THR (volatile uint8_t *)(UART + 0x00) // THR:transmitter holding register
</span> <span class="cp">#define UART_RHR (volatile uint8_t *)(UART + 0x00) // RHR:Receive holding register
</span> <span class="cp">#define UART_DLL (volatile uint8_t *)(UART + 0x00) // LSB of Divisor Latch (write mode)
</span> <span class="cp">#define UART_DLM (volatile uint8_t *)(UART + 0x01) // MSB of Divisor Latch (write mode)
</span> <span class="cp">#define UART_IER (volatile uint8_t *)(UART + 0x01) // Interrupt Enable Register
</span> <span class="cp">#define UART_LCR (volatile uint8_t *)(UART + 0x03) // Line Control Register
</span> <span class="cp">#define UART_LSR (volatile uint8_t *)(UART + 0x05) // LSR:line status register
</span> <span class="cp">#define UART_LSR_EMPTY_MASK 0x40                   // LSR Bit 6: Transmitter empty; both the THR and LSR are empty
</span></code></pre></div></div>

<h2 id="simulation">Simulation</h2>

<p>Let’s see the TinyOS interrupt handler in action. If you have followed the tutorial series continuously, you know the steps.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>07-ExterInterrupt 
make
</code></pre></div></div>
<p><code>
riscv32-unknown-elf-gcc -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32 -g -Wall -T os.ld -o os.elf start.s sys.s lib.c timer.c task.c os.c user.c trap.c lock.c plic.c
</code></p>

<p>Next, you can run the Virt and type letters into the terminal, which will generate interrupt requests.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make qemu
</code></pre></div></div>
<p><code>
Press Ctrl-A and then X to exit QEMU<br />
qemu-system-riscv32 -nographic -smp 4 -machine virt -bios none -kernel os.elf<br />
OS start<br />
OS: Activate next task<br />
Task0: Created!<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
external interruption!<br />
j<br />
Task0: Running...<br />
Task0: Running...<br />
external interruption!<br />
k<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
external interruption!<br />
j<br />
Task0: Running...<br />
external interruption!<br />
k<br />
external interruption!<br />
j<br />
Task0: Running...<br />
timer interruption!<br />
timer_handler: 1<br />
OS: Back to OS<br />
QEMU: Terminated<br />
</code></p>

<p>In this episode, we have looked at configuring external peripherals and generating interrupts with that. I hope this was an interesting episode since this basic functionality is required when you are dealing with embedded systems in the future.</p>]]></content><author><name>A·ru·nạ</name></author><category term="Firmware" /><category term="OS" /><category term="Tutorial" /><category term="Bare Metal" /><category term="How-to" /><summary type="html"><![CDATA[A computer needs to communicate with the external world to perform tasks. To facilitate this, we have peripheral hardware. When these peripherals need to talk to the operating system, we have interrupts. In this episode of TinyOS🐞 tutorial series, we will be looking at interrupts and how to use them.]]></summary></entry><entry><title type="html">TinyOS🐞: Spinlocks</title><link href="https://archfx.me/posts/2024/04/tinyos6/" rel="alternate" type="text/html" title="TinyOS🐞: Spinlocks" /><published>2024-04-07T00:00:00-07:00</published><updated>2024-04-07T00:00:00-07:00</updated><id>https://archfx.me/posts/2024/04/blog-post-21</id><content type="html" xml:base="https://archfx.me/posts/2024/04/tinyos6/"><![CDATA[<p>In this episode of  episode of the <strong>TinyOS</strong>🐞 tutorial series, we will be looking at how to protect critical sections in processes using spinlocks.</p>

<h2 id="what-is-a-spinlock">What is a Spinlock</h2>
<p>A spinlock is a synchronization mechanism used to protect shared resources (such as data structures) from being accessed simultaneously by multiple threads of execution. Unlike other synchronization primitives like mutexes or semaphores, which typically put threads to sleep when the resource they’re trying to access is unavailable, a spinlock causes a thread trying to acquire the lock to repeatedly “spin” in a loop (i.e., continuously checking the lock’s state) until it becomes available.</p>

<p>The basic idea behind a spinlock is simple: when a thread wants to acquire the lock, it checks to see if the lock is available. If it is, the thread acquires the lock and continues execution. If the lock is not available (i.e., another thread holds it), the thread continuously polls the lock until it becomes available, at which point it acquires the lock and proceeds.</p>

<h2 id="atomic-operations">Atomic operations</h2>

<p>Atomic operations can ensure that an operation will not be interrupted by other operations before completion. Taking RISC-V as an example, it provides RV32A Instruction set, which are all atomic operations (Atomic).</p>

<p>In order to avoid multiple Spinlocks accessing the same memory at the same time, atomic operations are used in the Spinlock to ensure correct locking logic implementation.</p>

<blockquote>
  <p>In fact, not only Spinlock, mutex lock also requires Atomic operation in implementation.</p>
</blockquote>

<h2 id="simple-spinlock-in-c-language">Simple Spinlock in C language</h2>

<p>Consider the following code:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">typedef</span> <span class="k">struct</span> <span class="n">spinlock</span><span class="p">{</span>
    <span class="k">volatile</span> <span class="n">uint</span> <span class="n">lock</span><span class="p">;</span>
<span class="p">}</span> <span class="n">spinlock_t</span><span class="p">;</span>
<span class="kt">void</span> <span class="nf">lock</span><span class="p">(</span><span class="n">spinlock_t</span> <span class="o">*</span><span class="n">lock</span><span class="p">){</span>
    <span class="k">while</span><span class="p">(</span><span class="n">xchg</span><span class="p">(</span><span class="n">lock</span><span class="err">−</span><span class="o">&gt;</span><span class="n">lock</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span> <span class="nf">unlock</span><span class="p">(</span><span class="n">spinlock_t</span> <span class="o">*</span><span class="n">lock</span><span class="p">){</span>
    <span class="n">lock</span><span class="o">-&gt;</span><span class="n">lock</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Through the sample code, you can notice a few points:</p>

<ul>
  <li><strong>Keyword <code class="language-plaintext highlighter-rouge">volatile</code></strong>: <code class="language-plaintext highlighter-rouge">volatile</code> keyword lets the compiler know that the variable may be accessed in unexpected circumstances, so do not optimize the variable’s instructions to avoid storing the result in the Register, but write it directly to memory.</li>
  <li><strong>Lock function</strong>: <a href=""><code class="language-plaintext highlighter-rouge">xchg(a,b)</code></a> The contents of the two variables a and b can be swapped, and the function is an atomic operation. When the lock value is not 0, the execution thread will spin and wait until the lock is 0 (that is, it can be locked )until.</li>
  <li><strong>Unlock function</strong>: Since only one thread can obtain the lock at the same time, there is no need to worry about preemption of access when unlocking. Because of this, the example does not use atomic operations.</li>
</ul>

<h2 id="simple-lock">Simple Lock</h2>

<p>First of all, since TinyOS is a Single Hart (hardware thread) operating system, in addition to using atomic operations, there is actually a very simple way to achieve the locking effect:</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">basic_lock</span><span class="p">()</span>
<span class="p">{</span>
  <span class="n">w_mstatus</span><span class="p">(</span><span class="n">r_mstatus</span><span class="p">()</span> <span class="o">&amp;</span> <span class="o">~</span><span class="n">MSTATUS_MIE</span><span class="p">);</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="n">basic_unlock</span><span class="p">()</span>
<span class="p">{</span>
  <span class="n">w_mstatus</span><span class="p">(</span><span class="n">r_mstatus</span><span class="p">()</span> <span class="o">|</span> <span class="n">MSTATUS_MIE</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In <a href="https://github.com/Archfx/tinyos/blob/master/06-Spinlock/lock.c">lock.c</a>, we implement a very simple lock. When we invoke <code class="language-plaintext highlighter-rouge">basic_lock()</code> in the program, the system’s machine mode interrupt mechanism will be turned off. In this way, we can ensure that no there are other programs accessing the Shared memory to avoid the occurrence of Race condition.</p>

<h2 id="spinlock-implementation">Spinlock Implementation</h2>

<p>The above lock has an obvious flaw: <strong>When the program that acquires the lock has not released the lock, the entire system will be blocked</strong>. In order to ensure that the operating system can still maintain the multi-tasking mechanism, we must implement a bit more complex lock :</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">typedef</span> <span class="k">struct</span> <span class="nc">lock</span>
<span class="p">{</span>
  <span class="k">volatile</span> <span class="kt">int</span> <span class="n">locked</span><span class="p">;</span>
<span class="p">}</span> <span class="n">lock_t</span><span class="p">;</span>

<span class="kt">void</span> <span class="n">lock_init</span><span class="p">(</span><span class="n">lock_t</span> <span class="o">*</span><span class="n">lock</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">lock</span><span class="o">-&gt;</span><span class="n">locked</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="n">lock_acquire</span><span class="p">(</span><span class="n">lock_t</span> <span class="o">*</span><span class="n">lock</span><span class="p">)</span>
<span class="p">{</span>
  <span class="k">for</span> <span class="p">(;;)</span>
  <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">atomic_swap</span><span class="p">(</span><span class="n">lock</span><span class="p">))</span>
    <span class="p">{</span>
      <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="n">lock_free</span><span class="p">(</span><span class="n">lock_t</span> <span class="o">*</span><span class="n">lock</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">lock</span><span class="o">-&gt;</span><span class="n">locked</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In fact, the above program code is basically the same as the previous example of Spinlock. When we implement it in the system, we only need to deal with one more troublesome problem, which is to implement the atomic swap action <code class="language-plaintext highlighter-rouge">atomic_swap()</code>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">.</span><span class="n">globl</span> <span class="n">atomic_swap</span>
<span class="p">.</span><span class="n">align</span> <span class="mi">4</span>
<span class="n">atomic_swap</span><span class="o">:</span>
        <span class="n">li</span> <span class="n">a5</span><span class="p">,</span> <span class="mi">1</span>
        <span class="n">amoswap</span><span class="p">.</span><span class="n">w</span><span class="p">.</span><span class="n">aq</span> <span class="n">a5</span><span class="p">,</span> <span class="n">a5</span><span class="p">,</span> <span class="mi">0</span><span class="p">(</span><span class="n">a0</span><span class="p">)</span>
        <span class="n">mv</span> <span class="n">a0</span><span class="p">,</span> <span class="n">a5</span>
        <span class="n">ret</span>
</code></pre></div></div>

<p>As shown in above assembly construct, we can read the lock in the lock structure, exchange it with the value <code class="language-plaintext highlighter-rouge">1</code>, and finally return the contents of the register <code class="language-plaintext highlighter-rouge">a5</code>.
Further summarizing the execution results of the program, we can draw two cases:</p>

<ol>
  <li><strong>Case 1- Successfully acquire the lock</strong>: When <code class="language-plaintext highlighter-rouge">lock-&gt;locked</code> is <code class="language-plaintext highlighter-rouge">0</code>, after the exchange through <code class="language-plaintext highlighter-rouge">amoswap.w.aq</code>, the value of <code class="language-plaintext highlighter-rouge">lock-&gt;locked</code> is <code class="language-plaintext highlighter-rouge">1</code> and the return value (Value of a5) is <code class="language-plaintext highlighter-rouge">0</code>:
    <div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">lock_acquire</span><span class="p">(</span><span class="n">lock_t</span> <span class="o">*</span><span class="n">lock</span><span class="p">)</span>
<span class="p">{</span>
  <span class="k">for</span> <span class="p">(;;)</span>
  <span class="p">{</span>
 <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">atomic_swap</span><span class="p">(</span><span class="n">lock</span><span class="p">))</span>
 <span class="p">{</span>
   <span class="k">break</span><span class="p">;</span>
 <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>    </div>
    <p>When the return value is <code class="language-plaintext highlighter-rouge">0</code>, <code class="language-plaintext highlighter-rouge">lock_acquire()</code> will successfully jump out of the infinite loop and enter Critical sections for execution.</p>
  </li>
  <li><strong>Case 2- No lock acquired</strong>: Otherwise, continue to try to obtain the lock in an infinite loop.</li>
</ol>

<h2 id="simulation">Simulation</h2>

<p>If you followed the <strong>TinyOS</strong> tutorial series contnously, you know how to run the simulation of the code. If you missed the first article about setting up the environment, you can check it from <a href="https://archfx.github.io/posts/2023/08/tinyos0/">here</a>.</p>

<p>Now let’s take a look at the system’s behaviour.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>tinyos/06-Spinlock 
make
</code></pre></div></div>
<p><code>
riscv32-unknown-elf-gcc -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32 -g -Wall -T os.ld -o os.elf start.s sys.s lib.c timer.c task.c os.c user.c trap.c lock.c
</code></p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make qemu
</code></pre></div></div>
<p><code>
Press Ctrl-A and then X to exit QEMU<br />
qemu-system-riscv32 -nographic -smp 4 -machine virt -bios none -kernel os.elf<br />
OS start<br />
OS: Activate next task<br />
Task0: Created!<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
timer interruption!<br />
timer_handler: 1<br />
OS: Back to OS<br />
&nbsp; <br />
OS: Activate next task<br />
Task1: Created!<br />
Task1: Running...<br />
Task1: Running...<br />
Task1: Running...<br />
Task1: Running...<br />
Task1: Running...<br />
Task1: Running...<br />
Task1: Running...<br />
Task1: Running...<br />
Task1: Running...<br />
Task1: Running...<br />
Task1: Running...<br />
Task1: Running...<br />
Task1: Running...<br />
Task1: Running...<br />
timer interruption!<br />
timer_handler: 2<br />
OS: Back to OS<br />
&nbsp; <br />
OS: Activate next task<br />
Task2: Created!<br />
The value of shared_var is: 550<br />
The value of shared_var is: 600<br />
The value of shared_var is: 650<br />
The value of shared_var is: 700<br />
The value of shared_var is: 750<br />
The value of shared_var is: 800<br />
The value of shared_var is: 850<br />
The value of shared_var is: 900<br />
The value of shared_var is: 950<br />
The value of shared_var is: 1000<br />
The value of shared_var is: 1050<br />
The value of shared_var is: 1100<br />
The value of shared_var is: 1150<br />
The value of shared_var is: 1200<br />
The value of shared_var is: 1250<br />
The value of shared_var is: 1300<br />
The value of shared_var is: 1350<br />
The value of shared_var is: 1400<br />
The value of shared_var is: 1450<br />
The value of shared_var is: 1500<br />
The value of shared_var is: 1550<br />
The value of shared_var is: 1600<br />
timer interruption!<br />
timer_handler: 3<br />
OS: Back to OS<br />
&nbsp; <br />
OS: Activate next task<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
Task0: Running...<br />
timer interruption!<br />
timer_handler: 4<br />
OS: Back to OS<br />
QEMU: Terminated<br />
</code></p>

<h2 id="debug-mode-and-breakpoint">Debug Mode and Breakpoint</h2>

<p>With the QEMU simulation, you can run the simulation in Debug mode with added break points. Following are the steps for running the TinyOS in debug mode.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make debug
</code></pre></div></div>
<p><code>
riscv32-unknown-elf-gcc -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32 -g -Wall -T os.ld -o os.elf start.s sys.s lib.c timer.c task.c os.c user.c trap.c lock.c
&nbsp; <br />
Press Ctrl-C and then input 'quit' to exit GDB and QEMU<br />
-------------------------------------------------------<br />
Reading symbols from os.elf...<br />
Breakpoint 1 at 0x80000000: file start.s, line 7.<br />
0x00001000 in ?? ()<br />
=&gt; 0x00001000:  97 02 00 00     auipc   t0,0x0<br />
&nbsp; <br />
Thread 1 hit Breakpoint 1, _start () at start.s:7<br />
7           csrr t0, mhartid                # read current hart id<br />
=&gt; 0x80000000 &lt;_start+0&gt;:       f3 22 40 f1     csrr    t0,mhartid<br />
(gdb)<br />
</code></p>

<p>You can set the breakpoint in any c file using the following command,</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">(</span>gdb<span class="o">)</span> b trap.c:27
</code></pre></div></div>
<p><code>
Breakpoint 2 at 0x80008f78: file trap.c, line 27.
(gdb)
</code></p>

<p>As the example above, when process running on trap.c, line 27 (Timer Interrupt).
The process will be suspended automatically until you press the key <code class="language-plaintext highlighter-rouge">c</code> (continue) or <code class="language-plaintext highlighter-rouge">s</code> (step).</p>]]></content><author><name>A·ru·nạ</name></author><category term="Firmware" /><category term="OS" /><category term="Tutorial" /><category term="Bare Metal" /><category term="How-to" /><summary type="html"><![CDATA[In this episode of episode of the TinyOS🐞 tutorial series, we will be looking at how to protect critical sections in processes using spinlocks.]]></summary></entry><entry><title type="html">TinyOS🐞: Preemptive Scheduling</title><link href="https://archfx.me/posts/2024/04/tinyos5/" rel="alternate" type="text/html" title="TinyOS🐞: Preemptive Scheduling" /><published>2024-04-06T00:00:00-07:00</published><updated>2024-04-06T00:00:00-07:00</updated><id>https://archfx.me/posts/2024/04/blog-post-20</id><content type="html" xml:base="https://archfx.me/posts/2024/04/tinyos5/"><![CDATA[<p>In the <a href="https://archfx.github.io/posts/2023/09/tinyos3/">MultiTasking</a> episode of the <strong>TinyOS</strong>🐞 tutorial series, we implemented “Cooperative Multitasking”. Next in <a href="https://archfx.github.io/posts/2023/09/tinyos4/">TimerInterrupt</a> episode, we discussed how the RISC-V time interrupt mechanism works. If you have missed them, I highly recommend going through them before proceeding.</p>

<p>In this episode, we plan to combine the two techniques of the above episodes to implement a “Preemptive” operating system with forced time interruption. Technically, TinyOS is going to be a real-time operating system (RTOS) at the end of this episode.</p>

<h2 id="simulation">Simulation</h2>

<p>As we discussed in earlier episodes, we know that where there are multple processes running parallely, they need share the same set of resources between them. So with that in mind, let’s run the simulation. Simulation steps are as usual.</p>

<p>If you missed the first article about setting up the environment, you can check it from <a href="https://archfx.github.io/posts/2023/08/tinyos0/">here</a>.</p>

<p>First let’s take a look at the system’s behaviour.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>tinyos/05-Preemptive
make
</code></pre></div></div>
<p><code>
riscv32-unknown-elf-gcc -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32 -T os.ld -o os.elf start.s sys.s lib.c timer.c task.c os.c user.c
</code></p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make qemu
</code></pre></div></div>
<p><code>
Press Ctrl-A and then X to exit QEMU<br />
qemu-system-riscv32 -nographic -smp 4 -machine virt -bios none -kernel os.elf<br />
OS start<br />
OS: Activate next task<br />
Task0: Created! <br />
Task0: Running... <br />
Task0: Running... <br />
Task0: Running... <br />
timer_handler: 1 <br />
OS: Back to OS <br />
&nbsp; <br />
OS: Activate next task <br />
Task1: Created! <br />
Task1: Running... <br />
Task1: Running... <br />
Task1: Running... <br />
timer_handler: 2 <br />
OS: Back to OS <br />
&nbsp; <br />
OS: Activate next task <br />
Task0: Running... <br />
Task0: Running... <br />
Task0: Running... <br />
timer_handler: 3 <br />
OS: Back to OS <br />
&nbsp; <br />
OS: Activate next task
Task1: Running... <br />
Task1: Running... <br />
Task1: Running... <br />
timer_handler: 4<br />
OS: Back to OS<br />
&nbsp; <br />
OS: Activate next task<br />
Task0: Running... <br />
Task0: Running... <br />
Task0: Running... <br />
QEMU: Terminated<br />
</code></p>

<p>As we can see, system switches the context between OS, Task0, and Task1 during the execution This situation is very similar to the simulation of <a href="https://archfx.github.io/posts/2023/09/tinyos3/">MultiTasking</a> episode, where both of which have the following execution sequence.</p>

<pre class="mermaid">
    stateDiagram-v2
    direction LR
    State1 :OS
	State2 : Task0
	State3 : OS
	State4 :Task1
	State5 : OS
	State6 : Task0
	State7 : OS
	State8 : Task1
	
    State1 --&gt; State2
	State2 --&gt; State3
	State3 --&gt; State4
	State4 --&gt; State5
	State5 --&gt; State6
	State6 --&gt; State7
	State7 --&gt; State8

</pre>

<p>The only difference is that the user process in <a href="https://archfx.github.io/posts/2023/09/tinyos3/">MultiTasking</a> episode must actively return control to the operating system through <code class="language-plaintext highlighter-rouge">os_kernel()</code>.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">user_task0</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task0: Created!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task0: Now, return to kernel mode</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="n">os_kernel</span><span class="p">();</span>
	<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task0: Running...</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="n">lib_delay</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
		<span class="n">os_kernel</span><span class="p">();</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>However, during this simulation, the user schedule does not need to be actively handed back to the OS, but the OS forces the switching action through time interruption.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">user_task0</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task0: Created!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task0: Running...</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="n">lib_delay</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The lib_delay in <a href="https://github.com/Archfx/tinyos/blob/master/05-Preemptive/lib.c">lib.c</a> is actually a delay loop and does not return control.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">lib_delay</span><span class="p">(</span><span class="k">volatile</span> <span class="kt">int</span> <span class="n">count</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">count</span> <span class="o">*=</span> <span class="mi">50000</span><span class="p">;</span>
	<span class="k">while</span> <span class="p">(</span><span class="n">count</span><span class="o">--</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>On the contrary, the operating system will forcefully take back control through time interruption. (Because lib_delay has a long delay, the operating system usually interrupts its <code class="language-plaintext highlighter-rouge">while (count--)</code> loop to take back control)</p>

<h2 id="os-kernel">OS Kernel</h2>

<p>The operating system <a href="https://github.com/Archfx/tinyos/blob/master/05-Preemptive/os.c">os.c</a> will initially call <code class="language-plaintext highlighter-rouge">user_init()</code> to allow the user to create tasks (in this example, user_task0 and user_task1 will be created in <a href="https://github.com/Archfx/tinyos/blob/master/05-Preemptive/user.c">user.c</a>.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">"os.h"</span><span class="cp">
</span>
<span class="kt">void</span> <span class="nf">user_task0</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task0: Created!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task0: Running...</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="n">lib_delay</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="n">user_task1</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task1: Created!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task1: Running...</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="n">lib_delay</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="n">user_init</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">task_create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">user_task0</span><span class="p">);</span>
	<span class="n">task_create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">user_task1</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Then the operating system will set the time interrupt through the <code class="language-plaintext highlighter-rouge">timer_init()</code> function in <code class="language-plaintext highlighter-rouge">os_start()</code>, and then enter the main loop of <code class="language-plaintext highlighter-rouge">os_main()</code>, which adopts Round-Robin scheduling. In Round robin scheduling each process is assigned a fixed time slice in a cyclic manner, ensuring fairness by giving each process equal time on the CPU regardless of its priority or execution time.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="cp">#include</span> <span class="cpf">"os.h"</span><span class="cp">
</span>
<span class="kt">void</span> <span class="nf">os_kernel</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">task_os</span><span class="p">();</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="n">os_start</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">lib_puts</span><span class="p">(</span><span class="s">"OS start</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="n">user_init</span><span class="p">();</span>
	<span class="n">timer_init</span><span class="p">();</span> <span class="c1">// start timer interrupt ...</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="n">os_main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">os_start</span><span class="p">();</span>

	<span class="kt">int</span> <span class="n">current_task</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">lib_puts</span><span class="p">(</span><span class="s">"OS: Activate next task</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="n">task_go</span><span class="p">(</span><span class="n">current_task</span><span class="p">);</span>
		<span class="n">lib_puts</span><span class="p">(</span><span class="s">"OS: Back to OS</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="n">current_task</span> <span class="o">=</span> <span class="p">(</span><span class="n">current_task</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="n">taskTop</span><span class="p">;</span> <span class="c1">// Round Robin Scheduling</span>
		<span class="n">lib_puts</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In the  interrupt mechanism  of <a href="https://github.com/Archfx/tinyos/blob/master/05-Preemptive/sys.s">sys.s</a>, we modified the interrupt vector table as below.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">.</span><span class="n">globl</span> <span class="n">trap_vector</span>
<span class="cp"># the trap vector base address must always be aligned on a 4-byte boundary
</span><span class="p">.</span><span class="n">align</span> <span class="mi">4</span>
<span class="n">trap_vector</span><span class="o">:</span>
	<span class="cp"># save context(registers).
</span>	<span class="n">csrrw</span>	<span class="n">t6</span><span class="p">,</span> <span class="n">mscratch</span><span class="p">,</span> <span class="n">t6</span>	<span class="err">#</span> <span class="n">swap</span> <span class="n">t6</span> <span class="n">and</span> <span class="n">mscratch</span>
        <span class="n">reg_save</span> <span class="n">t6</span>
	<span class="n">csrw</span>	<span class="n">mscratch</span><span class="p">,</span> <span class="n">t6</span>
	<span class="cp"># call the C trap handler in trap.c
</span>	<span class="n">csrr</span>	<span class="n">a0</span><span class="p">,</span> <span class="n">mepc</span>
	<span class="n">csrr</span>	<span class="n">a1</span><span class="p">,</span> <span class="n">mcause</span>
	<span class="n">call</span>	<span class="n">trap_handler</span>

	<span class="cp"># trap_handler will return the return address via a0.
</span>	<span class="n">csrw</span>	<span class="n">mepc</span><span class="p">,</span> <span class="n">a0</span>

	<span class="cp"># load context(registers).
</span>	<span class="n">csrr</span>	<span class="n">t6</span><span class="p">,</span> <span class="n">mscratch</span>
	<span class="n">reg_load</span> <span class="n">t6</span>
	<span class="n">mret</span>
</code></pre></div></div>

<p>Essentially what it does is when an interrupt occurs, the interrupt vector table <code class="language-plaintext highlighter-rouge">trap_vector()</code> will call <code class="language-plaintext highlighter-rouge">trap_handler()</code> in <a href="https://github.com/Archfx/tinyos/blob/master/05-Preemptive/trap.c">trap.c</a>.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">reg_t</span> <span class="nf">trap_handler</span><span class="p">(</span><span class="n">reg_t</span> <span class="n">epc</span><span class="p">,</span> <span class="n">reg_t</span> <span class="n">cause</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">reg_t</span> <span class="n">return_pc</span> <span class="o">=</span> <span class="n">epc</span><span class="p">;</span>
  <span class="n">reg_t</span> <span class="n">cause_code</span> <span class="o">=</span> <span class="n">cause</span> <span class="o">&amp;</span> <span class="mh">0xfff</span><span class="p">;</span>

  <span class="k">if</span> <span class="p">(</span><span class="n">cause</span> <span class="o">&amp;</span> <span class="mh">0x80000000</span><span class="p">)</span>
  <span class="p">{</span>
    <span class="cm">/* Asynchronous trap - interrupt */</span>
    <span class="k">switch</span> <span class="p">(</span><span class="n">cause_code</span><span class="p">)</span>
    <span class="p">{</span>
    <span class="k">case</span> <span class="mi">3</span><span class="p">:</span>
      <span class="n">lib_puts</span><span class="p">(</span><span class="s">"software interruption!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
      <span class="k">break</span><span class="p">;</span>
    <span class="k">case</span> <span class="mi">7</span><span class="p">:</span>
      <span class="n">lib_puts</span><span class="p">(</span><span class="s">"timer interruption!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
      <span class="c1">// disable machine-mode timer interrupts.</span>
      <span class="n">w_mie</span><span class="p">(</span><span class="o">~</span><span class="p">((</span><span class="o">~</span><span class="n">r_mie</span><span class="p">())</span> <span class="o">|</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">7</span><span class="p">)));</span>
      <span class="n">timer_handler</span><span class="p">();</span>
      <span class="n">return_pc</span> <span class="o">=</span> <span class="p">(</span><span class="n">reg_t</span><span class="p">)</span><span class="o">&amp;</span><span class="n">os_kernel</span><span class="p">;</span>
      <span class="c1">// enable machine-mode timer interrupts.</span>
      <span class="n">w_mie</span><span class="p">(</span><span class="n">r_mie</span><span class="p">()</span> <span class="o">|</span> <span class="n">MIE_MTIE</span><span class="p">);</span>
      <span class="k">break</span><span class="p">;</span>
    <span class="k">case</span> <span class="mi">11</span><span class="p">:</span>
      <span class="n">lib_puts</span><span class="p">(</span><span class="s">"external interruption!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
      <span class="k">break</span><span class="p">;</span>
    <span class="nl">default:</span>
      <span class="n">lib_puts</span><span class="p">(</span><span class="s">"unknown async exception!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
      <span class="k">break</span><span class="p">;</span>
    <span class="p">}</span>
  <span class="p">}</span>
  <span class="k">else</span>
  <span class="p">{</span>
    <span class="cm">/* Synchronous trap - exception */</span>
    <span class="n">lib_puts</span><span class="p">(</span><span class="s">"Sync exceptions!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
    <span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span>
    <span class="p">{</span>
      <span class="cm">/* code */</span>
    <span class="p">}</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="n">return_pc</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>After jumping to <code class="language-plaintext highlighter-rouge">trap_handler()</code>, it will call different handlers for different types of interrupts, so we can think of it as an interrupt dispatch task relay station.</p>

<pre class="mermaid">
graph LR
    C[trap_handler] --&gt; D[soft_handler]
    C --&gt; E[timer_handler]
    C --&gt; F[exter_handler]

</pre>

<p><code class="language-plaintext highlighter-rouge">trap_handler</code> can hand over interrupt processing to different handlers according to different interrupt types. This can greatly improve the scalability of the operating system.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">"timer.h"</span><span class="cp">
</span>
<span class="c1">// a scratch area per CPU for machine-mode timer interrupts.</span>
<span class="n">reg_t</span> <span class="n">timer_scratch</span><span class="p">[</span><span class="n">NCPU</span><span class="p">][</span><span class="mi">5</span><span class="p">];</span>

<span class="cp">#define interval 20000000 // cycles; about 2 second in qemu.
</span>
<span class="kt">void</span> <span class="nf">timer_init</span><span class="p">()</span>
<span class="p">{</span>
  <span class="c1">// each CPU has a separate source of timer interrupts.</span>
  <span class="kt">int</span> <span class="n">id</span> <span class="o">=</span> <span class="n">r_mhartid</span><span class="p">();</span>

  <span class="c1">// ask the CLINT for a timer interrupt.</span>
  <span class="c1">// int interval = 1000000; // cycles; about 1/10th second in qemu.</span>

  <span class="o">*</span><span class="p">(</span><span class="n">reg_t</span> <span class="o">*</span><span class="p">)</span><span class="n">CLINT_MTIMECMP</span><span class="p">(</span><span class="n">id</span><span class="p">)</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">reg_t</span> <span class="o">*</span><span class="p">)</span><span class="n">CLINT_MTIME</span> <span class="o">+</span> <span class="n">interval</span><span class="p">;</span>

  <span class="c1">// prepare information in scratch[] for timervec.</span>
  <span class="c1">// scratch[0..2] : space for timervec to save registers.</span>
  <span class="c1">// scratch[3] : address of CLINT MTIMECMP register.</span>
  <span class="c1">// scratch[4] : desired interval (in cycles) between timer interrupts.</span>
  <span class="n">reg_t</span> <span class="o">*</span><span class="n">scratch</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">timer_scratch</span><span class="p">[</span><span class="n">id</span><span class="p">][</span><span class="mi">0</span><span class="p">];</span>
  <span class="n">scratch</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">=</span> <span class="n">CLINT_MTIMECMP</span><span class="p">(</span><span class="n">id</span><span class="p">);</span>
  <span class="n">scratch</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="n">interval</span><span class="p">;</span>
  <span class="n">w_mscratch</span><span class="p">((</span><span class="n">reg_t</span><span class="p">)</span><span class="n">scratch</span><span class="p">);</span>

  <span class="c1">// enable machine-mode timer interrupts.</span>
  <span class="n">w_mie</span><span class="p">(</span><span class="n">r_mie</span><span class="p">()</span> <span class="o">|</span> <span class="n">MIE_MTIE</span><span class="p">);</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">int</span> <span class="n">timer_count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

<span class="kt">void</span> <span class="n">timer_handler</span><span class="p">()</span>
<span class="p">{</span>
  <span class="n">lib_printf</span><span class="p">(</span><span class="s">"timer_handler: %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="o">++</span><span class="n">timer_count</span><span class="p">);</span>
  <span class="kt">int</span> <span class="n">id</span> <span class="o">=</span> <span class="n">r_mhartid</span><span class="p">();</span>
  <span class="o">*</span><span class="p">(</span><span class="n">reg_t</span> <span class="o">*</span><span class="p">)</span><span class="n">CLINT_MTIMECMP</span><span class="p">(</span><span class="n">id</span><span class="p">)</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">reg_t</span> <span class="o">*</span><span class="p">)</span><span class="n">CLINT_MTIME</span> <span class="o">+</span> <span class="n">interval</span><span class="p">;</span>
<span class="p">}</span>

</code></pre></div></div>

<p>If you observe the function <code class="language-plaintext highlighter-rouge">timer_handler()</code> in <a href="https://github.com/Archfx/tinyos/blob/master/05-Preemptive/timer.c">timer.c</a>, you can see that it invokes reset <code class="language-plaintext highlighter-rouge">MTIMECMP</code>.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/* In trap_handler() */</span>
<span class="c1">// ...</span>
<span class="k">case</span> <span class="mi">7</span><span class="p">:</span>
      <span class="n">lib_puts</span><span class="p">(</span><span class="s">"timer interruption!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
      <span class="c1">// disable machine-mode timer interrupts.</span>
      <span class="n">w_mie</span><span class="p">(</span><span class="o">~</span><span class="p">((</span><span class="o">~</span><span class="n">r_mie</span><span class="p">())</span> <span class="o">|</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">7</span><span class="p">)));</span>
      <span class="n">timer_handler</span><span class="p">();</span>
      <span class="n">return_pc</span> <span class="o">=</span> <span class="p">(</span><span class="n">reg_t</span><span class="p">)</span><span class="o">&amp;</span><span class="n">os_kernel</span><span class="p">;</span>
      <span class="c1">// enable machine-mode timer interrupts.</span>
      <span class="n">w_mie</span><span class="p">(</span><span class="n">r_mie</span><span class="p">()</span> <span class="o">|</span> <span class="n">MIE_MTIE</span><span class="p">);</span>
      <span class="k">break</span><span class="p">;</span>
<span class="c1">// ...</span>
</code></pre></div></div>

<p>In order to avoid interrupt nesting in Timer Interrupt, <code class="language-plaintext highlighter-rouge">trap_handler()</code> will close the timer interrupt before processing the interrupt, and then open it again after the processing is completed.</p>

<p>After <code class="language-plaintext highlighter-rouge">timer_handler()</code> is executed, <code class="language-plaintext highlighter-rouge">trap_handler()</code> will point mepc to <code class="language-plaintext highlighter-rouge">os_kernel()</code> to achieve the task switching function.
  In other words, if the interrupt does not belong to Timer Interrupt, the Program counter will jump back to the state before entering the interrupt. This step is defined in <code class="language-plaintext highlighter-rouge">trap_vector()</code> as below.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">csrr</span>	<span class="n">a0</span><span class="p">,</span> <span class="n">mepc</span> <span class="err">#</span> <span class="n">a0</span> <span class="o">=&gt;</span> <span class="n">arg1</span> <span class="p">(</span><span class="n">return_pc</span><span class="p">)</span> <span class="n">of</span> <span class="n">trap_handler</span><span class="p">()</span>
</code></pre></div></div>

<blockquote>
  <p><strong>Note</strong>
In RISC-V, the parameters of the function will be first stored in the a0 - a7 registers. If the space is not enough, they will be stored in the Stack.
Among them, the a0 and a1 registers also serve as function return values.</p>
</blockquote>

<p>Finally, we import the trap and timer initialization actions when the Kernel is started as illustrated below.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">os_start</span><span class="p">()</span>
<span class="p">{</span>
	<span class="n">lib_puts</span><span class="p">(</span><span class="s">"OS start</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="n">user_init</span><span class="p">();</span>
	<span class="n">trap_init</span><span class="p">();</span>
	<span class="n">timer_init</span><span class="p">();</span> <span class="c1">// start timer interrupt ...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>By forcibly taking back control through time interruption, we don’t have to worry about a bully schedule taking over the CPU, and the system will not be stuck by the bully and completely paralyzed. This is the most important “schedule management mechanism” in modern operating systems.</p>

<h2 id="remarks">Remarks</h2>

<p>Although TinyOS is just a “tiny” embedded operating system, it still demonstrates the design principle of a specific and simple “preemptible operating system” through relatively streamlined code.</p>

<p>Of course, there is still a long way to go to learn “Operating System Design”. In particular,  TinyOS does not have a “File System”, and we haven’t even touched on the areas related to control and switching methods of supervisor mode and user mode in RISC-V. Further, OS needs to handle virtual memory mechanisms, so that processes cannot steal other process’s data.</p>

<p>Fortunately, you can learn more about these more complex mechanisms by studying <a href="https://github.com/mit-pdos/xv6-riscv">xv6-riscv</a>, a teaching operating system designed by MIT. The source code of xv6-riscv has a total of more than 8,000 lines, although not too few, xv6-riscv is a very streamlined system compared to modern Linux and Windows, which can run from millions to tens of millions of lines.</p>

<p>I hope this episode of <strong>TinyOS</strong> tutorial series gave you the basic understanging about how the preemptive multitasking is working on RISC-V environment. In the next episode let’s discuss about Spinlocks in RISC-V.</p>]]></content><author><name>A·ru·nạ</name></author><category term="Firmware" /><category term="OS" /><category term="Tutorial" /><category term="Bare Metal" /><category term="How-to" /><summary type="html"><![CDATA[In the MultiTasking episode of the TinyOS🐞 tutorial series, we implemented “Cooperative Multitasking”. Next in TimerInterrupt episode, we discussed how the RISC-V time interrupt mechanism works. If you have missed them, I highly recommend going through them before proceeding.]]></summary></entry><entry><title type="html">TinyOS🐞: Multitasking</title><link href="https://archfx.me/posts/2023/09/tinyos3/" rel="alternate" type="text/html" title="TinyOS🐞: Multitasking" /><published>2023-09-08T00:00:00-07:00</published><updated>2023-09-08T00:00:00-07:00</updated><id>https://archfx.me/posts/2023/09/blog-post-18</id><content type="html" xml:base="https://archfx.me/posts/2023/09/tinyos3/"><![CDATA[<p>In the previous episode <a href="https://archfx.github.io/posts/2023/08/tinyos2/">ContextSwitch</a> of <strong>TinyOS</strong>🐞, we introduced the context switching mechanism under the RISC-V architecture. In this episode we will be looking at Multitasking in our DIY operating system.</p>

<h2 id="cooperative-multitasking">Cooperative Multitasking</h2>

<p>Modern operating systems have a <strong>Preemptive</strong> function that forcibly terminates the process through timed interruption, so that when a certain process occupies the CPU for too long, it is forcibly interrupted and switched to another process for execution.</p>

<p>However, in a system without a time interruption mechanism, the operating system cannot interrupt the current execution process, so it must rely on each process to actively return control to the operating system in order to allow all processes to have a chance to execute. This notion of multi-tasking system that relies on an automatic return mechanism is called a <strong>Coorperative Multitasking</strong> system. Windows 3.1 launched by Microsoft in 1991, Macintosh OS version 8.0-9.2, as well as <a href="https://github.com/MannyPeterson/HeliOS">HeliOS</a> for Arduino MCU, are all operating systems that employee cooperative multitasking mechanisms.</p>

<p>As an illustration step in our TinyOS series, in this chapter, we will design a cooperative multitasking mechanism on a RISC-V processor.</p>

<h2 id="simulation">Simulation</h2>

<p>First let’s execute the program. For this you can navigate to the <a href="https://github.com/Archfx/tinyos/tree/master/03-MultiTasking">MultiTasking</a> folder of the cloned repository from the docker image. If you missed the first article about setting up the environment, you can check it from <a href="https://archfx.github.io/posts/2023/08/tinyos0/">here</a>.</p>

<p>First let’s take a look at the system’s performance.</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>tinyos/03-MultiTasking
make qemu
</code></pre></div></div>
<p><code>
Press Ctrl-A and then X to exit QEMU<br />
qemu-system-riscv32 -nographic -smp 4 -machine virt -bios none -kernel os.elf<br />
OS start<br />
OS: Activate next task<br />
Task0: Created!<br />
Task0: Now, return to kernel mode<br />
OS: Back to OS<br />
&nbsp; <br />
OS: Activate next task<br />
Task1: Created!<br />
Task1: Now, return to kernel mode<br />
OS: Back to OS<br />
&nbsp; <br />
OS: Activate next task<br />
Task0: Running...<br />
OS: Back to OS<br />
&nbsp; <br />
OS: Activate next task<br />
Task1: Running...<br />
OS: Back to OS<br />
&nbsp; <br />
OS: Activate next task<br />
Task0: Running...<br />
OS: Back to OS<br />
&nbsp; <br />
OS: Activate next task<br />
Task1: Running...<br />
OS: Back to OS<br />
&nbsp; <br />
OS: Activate next task<br />
Task0: Running...<br />
OS: Back to OS<br />
&nbsp; <br />
OS: Activate next task<br />
Task1: Running...<br />
OS: Back to OS<br />
&nbsp; <br />
OS: Activate next task<br />
Task0: Running...<br />
QEMU: Terminated<br />
</code></p>

<p>You can see that the system keeps switching between two tasks <code class="language-plaintext highlighter-rouge">Task0</code>, <code class="language-plaintext highlighter-rouge">Task1</code>, but the actual switching process is as follows:</p>

<pre class="mermaid">
    stateDiagram-v2
    direction LR
    State1 :OS
	State2 : Task0
	State3 : OS
	State4 :Task1
	State5 : OS
	State6 : Task0
	State7 : OS
	State8 : Task1
	
    State1 --&gt; State2
	State2 --&gt; State3
	State3 --&gt; State4
	State4 --&gt; State5
	State5 --&gt; State6
	State6 --&gt; State7
	State7 --&gt; State8

</pre>

<p>Operating system needs cordinate the two task to execute sequancially. In this episode, we will looking at the underlying logic of this implementation.</p>

<h2 id="user-tasks">User Tasks</h2>

<p>In <a href="https://github.com/Archfx/tinyos/blob/master/03-MultiTasking/user.c">user.c</a>, we define two tasks, <code class="language-plaintext highlighter-rouge">user_task0</code> and <code class="language-plaintext highlighter-rouge">user_task1</code>, and finally initialize these two tasks in the <code class="language-plaintext highlighter-rouge">user_init</code> function.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// user.c</span>
<span class="cp">#include</span> <span class="cpf">"os.h"</span><span class="cp">
</span>
<span class="kt">void</span> <span class="nf">user_task0</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task0: Created!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task0: Now, return to kernel mode</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="n">os_kernel</span><span class="p">();</span>
	<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task0: Running...</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="n">lib_delay</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
		<span class="n">os_kernel</span><span class="p">();</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">user_task1</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task1: Created!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task1: Now, return to kernel mode</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="n">os_kernel</span><span class="p">();</span>
	<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task1: Running...</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="n">lib_delay</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
		<span class="n">os_kernel</span><span class="p">();</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">user_init</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">task_create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">user_task0</span><span class="p">);</span>
	<span class="n">task_create</span><span class="p">(</span><span class="o">&amp;</span><span class="n">user_task1</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="os-kernel">OS Kernel</h2>

<p>Then, in the main program <a href="https://github.com/Archfx/tinyos/blob/master/03-MultiTasking/os.c">os.c</a> of the operating system, we use a while loop to arrange each process to be executed sequentially.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// os.c</span>
<span class="cp">#include</span> <span class="cpf">"os.h"</span><span class="cp">
</span>
<span class="kt">void</span> <span class="nf">os_kernel</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">task_os</span><span class="p">();</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="nf">os_start</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">lib_puts</span><span class="p">(</span><span class="s">"OS start</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="n">user_init</span><span class="p">();</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="nf">os_main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">os_start</span><span class="p">();</span>
	
	<span class="kt">int</span> <span class="n">current_task</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">lib_puts</span><span class="p">(</span><span class="s">"OS: Activate next task</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="n">task_go</span><span class="p">(</span><span class="n">current_task</span><span class="p">);</span>
		<span class="n">lib_puts</span><span class="p">(</span><span class="s">"OS: Back to OS</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="n">current_task</span> <span class="o">=</span> <span class="p">(</span><span class="n">current_task</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="n">taskTop</span><span class="p">;</span> <span class="c1">// Round Robin Scheduling</span>
		<span class="n">lib_puts</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The above scheduling method is in principle consistent with <a href="https://en.wikipedia.org/wiki/Round-robin_scheduling">Round Robin Scheduling</a>, but Round Robin Scheduling must be equipped with a timed interruption mechanism in principle, but the code in this episode has no timed interruptions, so it can only be said to be the Round Robin Scheduling of the collaborative multitasking version.</p>

<p>Cooperative multitasking must rely on each task to actively return control. For example, in <code class="language-plaintext highlighter-rouge">user_task0</code>, whenever the <code class="language-plaintext highlighter-rouge">os_kernel()</code> function is called, the context switching mechanism will be called to return control to the operating system.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">user_task0</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task0: Created!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task0: Now, return to kernel mode</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="n">os_kernel</span><span class="p">();</span>
	<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task0: Running...</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="n">lib_delay</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
		<span class="n">os_kernel</span><span class="p">();</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">os_kernel()</code> function of <a href="https://github.com/Archfx/tinyos/blob/master/03-MultiTasking/os.c">os.c</a> will call the <code class="language-plaintext highlighter-rouge">task_os()</code> of <a href="https://github.com/Archfx/tinyos/blob/master/03-MultiTasking/task.c">task.c</a></p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">os_kernel</span><span class="p">()</span> <span class="p">{</span>
	<span class="n">task_os</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>

<p>And <code class="language-plaintext highlighter-rouge">task_os()</code> will call sys_switch in assembly language <a href="https://github.com/Archfx/tinyos/blob/master/03-MultiTasking/sys.s">sys.s</a> to switch back to the operating system.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// switch back to os</span>
<span class="kt">void</span> <span class="nf">task_os</span><span class="p">()</span> <span class="p">{</span>
	<span class="k">struct</span> <span class="n">context</span> <span class="o">*</span><span class="n">ctx</span> <span class="o">=</span> <span class="n">ctx_now</span><span class="p">;</span>
	<span class="n">ctx_now</span> <span class="o">=</span> <span class="o">&amp;</span><span class="n">ctx_os</span><span class="p">;</span>
	<span class="n">sys_switch</span><span class="p">(</span><span class="n">ctx</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">ctx_os</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>So the whole system is executed in turn letting the other process to execute under the cooperation of <code class="language-plaintext highlighter-rouge">os_main()</code>, <code class="language-plaintext highlighter-rouge">user_task0()</code>, <code class="language-plaintext highlighter-rouge">user_task1()</code>.</p>

<p><code class="language-plaintext highlighter-rouge">os_main()</code> function in <a href="https://github.com/Archfx/tinyos/blob/master/03-MultiTasking/os.c">os.c</a> looks like this.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">os_main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">os_start</span><span class="p">();</span>
	
	<span class="kt">int</span> <span class="n">current_task</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">lib_puts</span><span class="p">(</span><span class="s">"OS: Activate next task</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="n">task_go</span><span class="p">(</span><span class="n">current_task</span><span class="p">);</span>
		<span class="n">lib_puts</span><span class="p">(</span><span class="s">"OS: Back to OS</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="n">current_task</span> <span class="o">=</span> <span class="p">(</span><span class="n">current_task</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">%</span> <span class="n">taskTop</span><span class="p">;</span> <span class="c1">// Round Robin Scheduling</span>
		<span class="n">lib_puts</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">user_task0()</code> and <code class="language-plaintext highlighter-rouge">user_task1()</code> functions in <a href="https://github.com/Archfx/tinyos/blob/master/03-MultiTasking/user.c">user.c</a> looks like this.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">user_task0</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task0: Created!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task0: Now, return to kernel mode</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="n">os_kernel</span><span class="p">();</span>
	<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task0: Running...</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="n">lib_delay</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
		<span class="n">os_kernel</span><span class="p">();</span>
	<span class="p">}</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="n">user_task1</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task1: Created!</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task1: Now, return to kernel mode</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
	<span class="n">os_kernel</span><span class="p">();</span>
	<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">lib_puts</span><span class="p">(</span><span class="s">"Task1: Running...</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
		<span class="n">lib_delay</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
		<span class="n">os_kernel</span><span class="p">();</span>
	<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The above is an example of a specific and micro cooperative multitasking system on the RISC-V processor. In the next episode of TinyOS🐞, let’s look at implementation of TimerIntterupts.</p>]]></content><author><name>A·ru·nạ</name></author><category term="Firmware" /><category term="OS" /><category term="Tutorial" /><category term="Bare Metal" /><category term="How-to" /><summary type="html"><![CDATA[In the previous episode ContextSwitch of TinyOS🐞, we introduced the context switching mechanism under the RISC-V architecture. In this episode we will be looking at Multitasking in our DIY operating system.]]></summary></entry><entry><title type="html">TinyOS🐞: TimerInterrupts</title><link href="https://archfx.me/posts/2023/09/tinyos4/" rel="alternate" type="text/html" title="TinyOS🐞: TimerInterrupts" /><published>2023-09-08T00:00:00-07:00</published><updated>2023-09-08T00:00:00-07:00</updated><id>https://archfx.me/posts/2023/09/blog-post-19</id><content type="html" xml:base="https://archfx.me/posts/2023/09/tinyos4/"><![CDATA[<p>In the previous episode <a href="https://archfx.github.io/posts/2023/09/tinyos3/">MultiTasking</a>, we implemented a operating system with cooperative multitasking. However, without the implementation of an interruption mechanism, our system cannot support preemptive multitasking.</p>

<p>This episode will lay the foundation for a “Preemptive Multitasking System” by introducing the utilization of the “Time Interrupt Mechanism” in RISC-V processors. Through time interrupts, we gain the ability to regain control at predefined intervals, ensuring that a third-party application cannot indefinitely seize control of the system without yielding control back to the operating system.</p>

<h2 id="main-concepts-for-timerinterrupts">Main Concepts for TimerInterrupts</h2>

<p>Before learning how the system implements the time interruption mechanism, we must first understand a few things:</p>

<ul>
  <li>Generating Timer Interrupts</li>
  <li>Interrupt Vector Table</li>
  <li>Control and Status Registers (CSR)</li>
</ul>

<p>Lets go throgh each of the concept one by one.</p>

<h3 id="generating-timer-interrupts">Generating Timer Interrupts</h3>

<p>The RISC-V architecture specifies that the system platform must include a timer, and this timer must feature two 64-bit registers: <code class="language-plaintext highlighter-rouge">mtime</code> and <code class="language-plaintext highlighter-rouge">mtimecmp</code>. The purpose of these registers is as follows:</p>

<ol>
  <li>
    <p><code class="language-plaintext highlighter-rouge">mtime</code> (Machine Time): This register is utilized to keep track of the current counter value of the timer. It serves as a continuously incrementing counter, recording the passage of time in the system.</p>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">mtimecmp</code> (Machine Time Compare): The mtimecmp register is employed to set a comparison value against which the value of mtime is compared. When the value of mtime becomes greater than the value stored in mtimecmp, an interrupt is triggered.</p>
  </li>
</ol>

<p>These registers are integral for implementing time-based interrupt handling in RISC-V systems. By comparing mtime with the value stored in mtimecmp, the system can generate interrupts at specific time intervals, facilitating various timing-related tasks and enabling features such as preemptive multitasking and real-time scheduling.
You can find the definitions for these two registers in <a href="https://github.com/Archfx/tinyos/blob/master/04-TimerInterrupt/riscv.h">riscv.h</a>:</p>

<p>After understanding the mechanism for generating a Timer interrupt, we will examine a piece of code that defines the time interval (Interval) for each interrupt trigger in the upcoming explanation.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// ================== Timer Interrput ====================</span>

<span class="cp">#define NCPU 8             // maximum number of CPUs
#define CLINT 0x2000000
#define CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 4*(hartid))
#define CLINT_MTIME (CLINT + 0xBFF8) // cycles since boot.
</span></code></pre></div></div>

<p>Additionally, during system initialization, it’s essential to enable the Timer interrupt. This can be achieved by setting the corresponding field in the <code class="language-plaintext highlighter-rouge">mie</code> (Machine Interrupt Enable) register to 1.</p>

<h3 id="what-is-the-interrupt-vector-table">What is the Interrupt Vector Table?</h3>

<p>The interrupt vector table is a data structure managed by the system program. It serves as a mapping between interrupt numbers or types and their corresponding interrupt handlers. When a specific interrupt or exception occurs, the system will look up the corresponding Interrupt Handler in this table.</p>

<p>Here’s how it works:</p>

<ol>
  <li>
    <p>When an interrupt, such as a time interrupt, occurs, the processor first stops executing the current program’s instructions.</p>
  </li>
  <li>
    <p>It then looks up the interrupt or exception type in the interrupt vector table to find the associated Interrupt_Handler.</p>
  </li>
  <li>
    <p>The processor transfers control to the Interrupt_Handler, which is a predefined piece of code responsible for handling that specific type of interrupt or exception.</p>
  </li>
  <li>
    <p>The Interrupt_Handler performs the necessary processing, which may include saving the current context, handling the interrupt’s specific tasks, and eventually returning control to the interrupted program.</p>
  </li>
  <li>
    <p>After completing the handling of the interrupt, the processor jumps back to the original instruction address in the interrupted program, allowing it to continue execution as if the interrupt never occurred.</p>
  </li>
</ol>

<p>This mechanism is essential for managing and responding to various interrupts and exceptions in a systematic and controlled manner, ensuring that the system remains stable and responsive. The interrupt vector table plays a crucial role in facilitating this process by directing the processor to the appropriate interrupt handling routines.</p>

<blockquote>
  <p>Note:
When an exception or interrupt occurs, the processor will stop the current process, point the address of the Program counter to the address pointed by <code class="language-plaintext highlighter-rouge">mtvec</code> and start execution. Such behavior is like actively jumping into a trap. Therefore, this action is defined as Trap in the RISC-V architecture. In the xv6 (risc-v) operating system, we can also find a series of Operations to handle Interrupt (mostly defined in Trap.c).</p>
</blockquote>

<h3 id="control-and-status-registers-csr">Control and Status Registers (CSR)</h3>

<p>The RISC-V architecture encompasses numerous registers, including a category known as Control and Status Registers (CSRs), as highlighted in the title. CSRs serve the crucial role of configuring and recording the processor’s operational status.</p>

<ul>
  <li>
    <p>CSR (Control and Status Registers):</p>

    <ul>
      <li><code class="language-plaintext highlighter-rouge">mtvec</code>: This register specifies the address that the Program Counter (PC) will jump to when an exception occurs, allowing exception handling to begin.</li>
      <li><code class="language-plaintext highlighter-rouge">mcause</code>: It records the reason for encountering an exception or anomaly.</li>
      <li><code class="language-plaintext highlighter-rouge">mtval</code>: This register is used to store additional information or messages related to the encountered exception.</li>
      <li><code class="language-plaintext highlighter-rouge">mepc</code>: Before entering an exception, it holds the address pointed to by the PC, which can be read to resume execution after handling the exception.</li>
      <li><code class="language-plaintext highlighter-rouge">mstatus</code>: This register’s fields are updated by hardware when an exception is entered, reflecting various status changes.</li>
      <li><code class="language-plaintext highlighter-rouge">mie</code>: It determines whether interrupts are enabled or disabled.</li>
      <li><code class="language-plaintext highlighter-rouge">mip</code>: This register indicates the pending status of different types of interrupts.</li>
    </ul>
  </li>
  <li>
    <p>Memory Address Mapped:</p>

    <ul>
      <li><code class="language-plaintext highlighter-rouge">mtime</code>: Records the current value of the timer.</li>
      <li><code class="language-plaintext highlighter-rouge">mtimecmp</code>: Stores a comparison value for the timer, against which mtime is compared to generate timer interrupts.</li>
      <li><code class="language-plaintext highlighter-rouge">msip</code>: Used for generating or clearing software interrupts.</li>
      <li>Platform-Level Interrupt Controller (PLIC): This external hardware component handles and manages interrupts from various sources and devices in the system, ensuring that they are appropriately routed to the processor for handling.</li>
    </ul>
  </li>
</ul>

<p>In addition, RISC-V defines a series of instructions that allow developers to operate the CSR register:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">csrs</code>: Set the specified bit in the CSR to 1.</li>
</ul>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">csrsi</span> <span class="n">mstatus</span><span class="p">,</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">2</span><span class="p">)</span>
</code></pre></div></div>

<p>The above command will set the third position of mstatus from the LSB to 1.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">csrc</code>
Set the specified bit in the CSR to 0.</li>
</ul>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">csrsi</span> <span class="n">mstatus</span><span class="p">,</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">2</span><span class="p">)</span>
</code></pre></div></div>

<p>The above instruction will set the third position of mstatus from the LSB to 0.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">csrr</code>[c|s]
Read the value of CSR into the general scratchpad.</li>
</ul>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">csrr</span> <span class="n">to</span><span class="p">,</span> <span class="n">mscratch</span>
</code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">csrw</code>
Write the value of the general scratchpad to the CSR.</li>
</ul>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">csrw</span> <span class="n">mepc</span><span class="p">,</span> <span class="n">a0</span>
</code></pre></div></div>

<ul>
  <li><code class="language-plaintext highlighter-rouge">csrrw</code>[i]
Write the value of csr to rd and the value of rs1 to csr .</li>
</ul>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">csrrw</span> <span class="n">rd</span><span class="p">,</span> <span class="n">csr</span><span class="p">,</span> <span class="n">rs1</span><span class="o">/</span><span class="n">imm</span>
</code></pre></div></div>

<p>Think about it from another perspective:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">csrrw</span> <span class="n">t6</span><span class="p">,</span> <span class="n">mscratch</span><span class="p">,</span> <span class="n">t6</span>
</code></pre></div></div>

<p>The above operation can interchange the values ​​of register <code class="language-plaintext highlighter-rouge">t6</code> and <code class="language-plaintext highlighter-rouge">mscratch</code>.</p>

<h2 id="simulation">Simulation</h2>

<p>You can clone the <a href="https://github.com/Archfx/tinyos">tinyos</a> repository if you havent already. If you missed the introduction episode of this series, you can check it out from <a href="https://archfx.github.io/posts/2023/08/tinyos0/">here</a>. Then from the docker environment, navegate to <code class="language-plaintext highlighter-rouge">04-TimerInterrupt</code> folder on the mounted repo. After you use make clean, make and other commands to build the project, you can use make qemu to start simulation. The results are as follows:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>make
</code></pre></div></div>

<p><code>riscv32-unknown-elf-gcc -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32 -T os.ld -o os.elf start.s sys.s lib.c timer.c os.c</code></p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>makeqemu
</code></pre></div></div>
<p><code>
Press Ctrl-A and then X to exit QEMU<br />
qemu-system-riscv32 -nographic -smp 4 -machine virt -bios none -kernel os.elf<br />
OS start<br />
timer_handler: 1<br />
timer_handler: 2<br />
timer_handler: 3<br />
timer_handler: 4<br />
timer_handler: 5<br />
timer_handler: 6<br />
timer_handler: 7<br />
timer_handler: 8<br />
timer_handler: 9<br />
</code></p>

<p>The system will consistantly print out a message like <code class="language-plaintext highlighter-rouge">timer_handler: i</code> about once per second, which means that the time interrupt mechanism is successfully started and interrupts are performed regularly.</p>

<h2 id="discussion">Discussion</h2>

<p>Before explaining time interruption, let us first take a look at the contents of the operating system main program <a href="https://github.com/Archfx/tinyos/blob/master/04-TimerInterrupt/os.c">os.c</a>.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">"os.h"</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">os_main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">lib_puts</span><span class="p">(</span><span class="s">"OS start</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
<span class="n">timer_init</span><span class="p">();</span> <span class="c1">// start timer interrupt ...</span>
<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{}</span> <span class="c1">// os : do nothing, just loop!</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Basically, after this program prints <code class="language-plaintext highlighter-rouge">OS start</code>, it starts the time interrupt, and then enters the os_loop() infinite loop function and gets stuck.</p>

<p>But why does the system print a message like <code class="language-plaintext highlighter-rouge">timer_handler: i</code> later?</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>timer_handler: 1
timer_handler: 2
timer_handler: 3
</code></pre></div></div>

<p>This is of course caused by the time interruption mechanism!</p>

<p>Let’s take a look at the contents of <a href="https://github.com/Archfx/tinyos/blob/master/04-TimerInterrupt/timer.c">timer.c</a>. Please pay special attention to the line <code class="language-plaintext highlighter-rouge">w_mtvec((reg_t)sys_timer)</code>. When a time interrupt occurs, the program will jump to the <code class="language-plaintext highlighter-rouge">sys_timer</code> macro in <a href="https://github.com/Archfx/tinyos/blob/master/04-TimerInterrupt/sys.s">sys.s</a>.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">"timer.h"</span><span class="cp">
</span>
<span class="cp">#define interval 10000000 // cycles; about 1 second in qemu.
</span>
<span class="kt">void</span> <span class="nf">timer_init</span><span class="p">()</span>
<span class="p">{</span>
  <span class="c1">// each CPU has a separate source of timer interrupts.</span>
  <span class="kt">int</span> <span class="n">id</span> <span class="o">=</span> <span class="n">r_mhartid</span><span class="p">();</span>

  <span class="c1">// ask the CLINT for a timer interrupt.</span>
  <span class="o">*</span><span class="p">(</span><span class="n">reg_t</span><span class="o">*</span><span class="p">)</span><span class="n">CLINT_MTIMECMP</span><span class="p">(</span><span class="n">id</span><span class="p">)</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">reg_t</span><span class="o">*</span><span class="p">)</span><span class="n">CLINT_MTIME</span> <span class="o">+</span> <span class="n">interval</span><span class="p">;</span>

  <span class="c1">// set the machine-mode trap handler.</span>
  <span class="n">w_mtvec</span><span class="p">((</span><span class="n">reg_t</span><span class="p">)</span><span class="n">sys_timer</span><span class="p">);</span>

  <span class="c1">// enable machine-mode interrupts.</span>
  <span class="n">w_mstatus</span><span class="p">(</span><span class="n">r_mstatus</span><span class="p">()</span> <span class="o">|</span> <span class="n">MSTATUS_MIE</span><span class="p">);</span>

  <span class="c1">// enable machine-mode timer interrupts.</span>
  <span class="n">w_mie</span><span class="p">(</span><span class="n">r_mie</span><span class="p">()</span> <span class="o">|</span> <span class="n">MIE_MTIE</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">sys_timer</code> function in <a href="https://github.com/Archfx/tinyos/blob/master/04-TimerInterrupt/sys.s">sys.s</a> will use the csrr privileged instruction to temporarily store the <code class="language-plaintext highlighter-rouge">mepc</code> privileged register (the address that stores the interrupt point) in <code class="language-plaintext highlighter-rouge">a0</code> for storage. After<code class="language-plaintext highlighter-rouge"> timer_handler() </code>is executed, it can do a <code class="language-plaintext highlighter-rouge">mret</code> return to the interruption point.</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nl">sys_timer:</span>
	<span class="cp"># call the C timer_handler(reg_t epc, reg_t cause)
</span>	<span class="n">csrr</span>	<span class="n">a0</span><span class="p">,</span> <span class="n">mepc</span>
	<span class="n">csrr</span>	<span class="n">a1</span><span class="p">,</span> <span class="n">mcause</span>
	<span class="n">call</span>	<span class="n">timer_handler</span>

	<span class="cp"># timer_handler will return the return address via a0.
</span>	<span class="n">csrw</span>	<span class="n">mepc</span><span class="p">,</span> <span class="n">a0</span>

	<span class="n">mret</span> <span class="err">#</span> <span class="n">back</span> <span class="n">to</span> <span class="n">interrupt</span> <span class="n">location</span> <span class="p">(</span><span class="n">pc</span><span class="o">=</span><span class="n">mepc</span><span class="p">)</span>
</code></pre></div></div>

<p>Note that RISC-V defines three execution modes in their privilage level extention, namely “machine mode, super mode and user mode”.</p>

<p>All <strong>TinyOS</strong> tutorials  are executed in machine mode, and super mode (user mode is not used).</p>

<p><code class="language-plaintext highlighter-rouge">mepc</code> means that when an interrupt occurs in machine mode, the hardware will automatically execute the action of <code class="language-plaintext highlighter-rouge">mepc=pc</code>.</p>

<p>When <code class="language-plaintext highlighter-rouge">sys_timer</code> executes <code class="language-plaintext highlighter-rouge">mret</code>, the hardware will execute the action of <code class="language-plaintext highlighter-rouge">pc=mepc</code>, and then jump back to the original interruption point to continue execution. (As if nothing happened)</p>

<p>I’ve provided a basic overview of the RISC-V interrupt mechanism. However, to gain a deeper understanding of the process, it’s crucial to understand the machine mode-related privilege registers of the RISC-V processor, including <code class="language-plaintext highlighter-rouge">mhartid</code> (processor core identifier), <code class="language-plaintext highlighter-rouge">mstatus</code> (status register), <code class="language-plaintext highlighter-rouge">mie</code> (interrupt enable register), and more.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define interval 10000000 // cycles; about 1 second in qemu.
</span>
<span class="kt">void</span> <span class="nf">timer_init</span><span class="p">()</span>
<span class="p">{</span>
  <span class="c1">// each CPU has a separate source of timer interrupts.</span>
  <span class="kt">int</span> <span class="n">id</span> <span class="o">=</span> <span class="n">r_mhartid</span><span class="p">();</span>

  <span class="c1">// ask the CLINT for a timer interrupt.</span>
  <span class="o">*</span><span class="p">(</span><span class="n">reg_t</span><span class="o">*</span><span class="p">)</span><span class="n">CLINT_MTIMECMP</span><span class="p">(</span><span class="n">id</span><span class="p">)</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">reg_t</span><span class="o">*</span><span class="p">)</span><span class="n">CLINT_MTIME</span> <span class="o">+</span> <span class="n">interval</span><span class="p">;</span>

  <span class="c1">// set the machine-mode trap handler.</span>
  <span class="n">w_mtvec</span><span class="p">((</span><span class="n">reg_t</span><span class="p">)</span><span class="n">sys_timer</span><span class="p">);</span>

  <span class="c1">// enable machine-mode interrupts.</span>
  <span class="n">w_mstatus</span><span class="p">(</span><span class="n">r_mstatus</span><span class="p">()</span> <span class="o">|</span> <span class="n">MSTATUS_MIE</span><span class="p">);</span>

  <span class="c1">// enable machine-mode timer interrupts.</span>
  <span class="n">w_mie</span><span class="p">(</span><span class="n">r_mie</span><span class="p">()</span> <span class="o">|</span> <span class="n">MIE_MTIE</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>In addition, it is required to understand the memory mapping area in the RISC-V QEMU virtual machine, such as <code class="language-plaintext highlighter-rouge">CLINT_MTIME</code>, <code class="language-plaintext highlighter-rouge">CLINT_MTIMECMP</code>, etc.</p>

<p>The time interrupt mechanism of RISC-V is to compare the two values ​​​​of <code class="language-plaintext highlighter-rouge">CLINT_MTIME</code> and <code class="language-plaintext highlighter-rouge">CLINT_MTIMECMP</code>. When <code class="language-plaintext highlighter-rouge">CLINT_MTIME</code> exceeds <code class="language-plaintext highlighter-rouge">CLINT_MTIMECMP</code>, an interrupt occurs.</p>

<p>Therefore, the <code class="language-plaintext highlighter-rouge">timer_init()</code> function has the following instructions</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="o">*</span><span class="p">(</span><span class="n">reg_t</span><span class="o">*</span><span class="p">)</span><span class="n">CLINT_MTIMECMP</span><span class="p">(</span><span class="n">id</span><span class="p">)</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">reg_t</span><span class="o">*</span><span class="p">)</span><span class="n">CLINT_MTIME</span> <span class="o">+</span> <span class="n">interval</span><span class="p">;</span>
</code></pre></div></div>

<p>This command is to set the first interruption time.</p>

<p>Similarly, in timer_handler of <a href="https://github.com/Archfx/tinyos/blob/master/04-TimerInterrupt/timer.c">timer.c</a>, you also need to set the next interrupt time as illustrated in below code.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">reg_t</span> <span class="nf">timer_handler</span><span class="p">(</span><span class="n">reg_t</span> <span class="n">epc</span><span class="p">,</span> <span class="n">reg_t</span> <span class="n">cause</span><span class="p">)</span>
<span class="p">{</span>
  <span class="n">reg_t</span> <span class="n">return_pc</span> <span class="o">=</span> <span class="n">epc</span><span class="p">;</span>
  <span class="c1">// disable machine-mode timer interrupts.</span>
  <span class="n">w_mie</span><span class="p">(</span><span class="o">~</span><span class="p">((</span><span class="o">~</span><span class="n">r_mie</span><span class="p">())</span> <span class="o">|</span> <span class="p">(</span><span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">7</span><span class="p">)));</span>
  <span class="n">lib_printf</span><span class="p">(</span><span class="s">"timer_handler: %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="o">++</span><span class="n">timer_count</span><span class="p">);</span>
  <span class="kt">int</span> <span class="n">id</span> <span class="o">=</span> <span class="n">r_mhartid</span><span class="p">();</span>
  <span class="o">*</span><span class="p">(</span><span class="n">reg_t</span> <span class="o">*</span><span class="p">)</span><span class="n">CLINT_MTIMECMP</span><span class="p">(</span><span class="n">id</span><span class="p">)</span> <span class="o">=</span> <span class="o">*</span><span class="p">(</span><span class="n">reg_t</span> <span class="o">*</span><span class="p">)</span><span class="n">CLINT_MTIME</span> <span class="o">+</span> <span class="n">interval</span><span class="p">;</span>
  <span class="c1">// enable machine-mode timer interrupts.</span>
  <span class="n">w_mie</span><span class="p">(</span><span class="n">r_mie</span><span class="p">()</span> <span class="o">|</span> <span class="n">MIE_MTIE</span><span class="p">);</span>
  <span class="k">return</span> <span class="n">return_pc</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In this way, the next time the <code class="language-plaintext highlighter-rouge">CLINT_MTIMECMP</code> time comes, <code class="language-plaintext highlighter-rouge">CLINT_MTIME</code> will be greater than <code class="language-plaintext highlighter-rouge">CLINT_MTIMECMP</code>, and the interrupt will occur again.</p>

<p>In this episode of <strong>TinyOS</strong>, we looked the process of generating TimerInterrupts. This is a huge increment of the process of implementing preemptive muti tasking. In the Next episode, we will be specifically looking at that!</p>]]></content><author><name>A·ru·nạ</name></author><category term="Firmware" /><category term="OS" /><category term="Tutorial" /><category term="Bare Metal" /><category term="How-to" /><summary type="html"><![CDATA[In the previous episode MultiTasking, we implemented a operating system with cooperative multitasking. However, without the implementation of an interruption mechanism, our system cannot support preemptive multitasking.]]></summary></entry></feed>