<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://swordart.blog/</id>
  <title type="text">Art</title>
  <subtitle type="text">Home</subtitle>
  <updated>2026-09-25T00:00:00.000Z</updated>
  <author><name>Art</name></author>
  <link rel="alternate" href="https://swordart.blog/"/>
  <link rel="self" href="https://swordart.blog/atom.xml"/>
  <generator uri="https://github.com/CuteLeaf/Firefly">Firefly v6.16.8</generator>
    <entry>
      <id>https://swordart.blog/posts/yocto/</id>
      <title type="text">Yocto: Build Your Own Linux Distro</title>
      <published>2026-09-25T00:00:00.000Z</published>
      <updated>2026-09-25T00:00:00.000Z</updated>
      <author><name>Art</name></author>
      <link rel="alternate" href="https://swordart.blog/posts/yocto/"/>
      <summary type="text">What Yocto is, and how it builds a Linux distro for an embedded device.</summary>
      <content type="html"><![CDATA[<p>Yocto Project không phải là một bản Linux, mà là một bộ công cụ và quy trình để tự tạo ra bản Linux của riêng mình cho thiết bị nhúng. Chính dự án tự mô tả điều này bằng một câu hay được trích: nó không phải một distribution nhúng, nó tạo ra distribution tùy biến cho bạn. Yocto ra đời năm 2010 dưới sự bảo trợ của Linux Foundation. Mục tiêu là để các hãng chip, nhà sản xuất thiết bị và cộng đồng dùng chung một nền tảng build, thay vì mỗi nơi tự viết script riêng rồi tự vật lộn với nó.</p>
<p>Có thể hình dung Ubuntu hay Debian như món ăn đã nấu sẵn: bạn nhận về và dùng, thêm bớt chút gia vị bằng trình quản lý gói. Yocto thì trao cho bạn cả căn bếp. Có sách công thức cho từng nguyên liệu (kernel, bootloader, glibc, busybox, systemd, Qt…), có đầu bếp đọc công thức và nấu theo đúng thứ tự phụ thuộc. Món cuối cùng được nấu từ mã nguồn, cho đúng kiến trúc CPU, đúng board, và chỉ có những gì bạn cần. Nhờ vậy một image Yocto có thể nhỏ vài chục MB, hoặc đầy đủ tới mức có giao diện đồ họa và trình duyệt, tùy người thiết kế.</p>
<section><h2>Các thành phần<a href="#các-thành-phần"><span>#</span></a></h2><p>Căn bếp ấy gồm vài thành phần mà người mới thường nghe lẫn lộn:</p><figcaption>Bảng 1: Các thành phần của Yocto</figcaption>

<table><thead><tr><th>Thành phần</th><th>Vai trò</th></tr></thead><tbody><tr><td>BitBake</td><td>Công cụ thực thi tác vụ, là “đầu bếp”. Nó đọc metadata, dựng đồ thị phụ thuộc, rồi chạy song song các bước fetch, unpack, patch, configure, compile, install, package cho từng gói</td></tr><tr><td>Recipe (<code>.bb</code>)</td><td>Công thức cho một phần mềm: lấy source ở đâu, áp patch nào, build thế nào, cài vào đâu</td></tr><tr><td><code>.bbappend</code></td><td>Phần sửa bổ sung cho một recipe có sẵn mà không phải đụng vào file gốc, ví dụ thêm patch hay đổi cấu hình kernel</td></tr><tr><td>Class (<code>.bbclass</code>)</td><td>Logic dùng chung, ví dụ cách build một dự án CMake, autotools hay kernel module</td></tr><tr><td>Layer (<code>meta-*</code>)</td><td>Một tập recipe, class và cấu hình có cùng chủ đề. Các layer xếp chồng lên nhau, layer trên ghi đè hoặc mở rộng layer dưới</td></tr><tr><td>OpenEmbedded-Core</td><td>Layer nền tảng chứa các recipe cốt lõi mà hầu như mọi hệ thống đều cần</td></tr><tr><td>Poky</td><td>Bản phân phối tham chiếu của Yocto, gói sẵn BitBake, OE-Core và một layer mẫu để bắt đầu</td></tr><tr><td>BSP layer</td><td>Layer do hãng chip hoặc hãng board cung cấp (như <code>meta-ti</code>, <code>meta-raspberrypi</code>, <code>meta-freescale</code>…), chứa kernel, bootloader, firmware và device tree cho phần cứng cụ thể</td></tr><tr><td><code>MACHINE</code> / <code>DISTRO</code> / <code>local.conf</code></td><td>Ba lớp cấu hình: build cho phần cứng nào, theo chính sách phân phối nào, và các tùy chỉnh cục bộ của người build</td></tr></tbody></table></section>
<section><h2>Cấu trúc thư mục<a href="#cấu-trúc-thư-mục"><span>#</span></a></h2><p>Cấu trúc thư mục cơ bản khi mới <code>repo sync</code>:</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>yocto-bsp/</span></div></div><div><div><div>2</div></div><div><span>├── .repo/</span></div></div><div><div><div>3</div></div><div><span>│   ├── manifests/            ← git repo chứa các file manifest</span></div></div><div><div><div>4</div></div><div><span>│   ├── manifest.xml          ← manifest đang có hiệu lực</span></div></div><div><div><div>5</div></div><div><span>│   ├── repo/                 ← mã nguồn của chính công cụ repo</span></div></div><div><div><div>6</div></div><div><span>│   ├── projects/             ← thư mục .git thật của từng project</span></div></div><div><div><div>7</div></div><div><span>│   └── project-objects/</span></div></div><div><div><div>8</div></div><div><span>├── setup-environment         ← sinh ra nhờ &lt;copyfile&gt; trong manifest</span></div></div><div><div><div>9</div></div><div><span>└── sources/</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>├── poky/</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>│   ├── bitbake/</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>│   ├── meta/             ← OpenEmbedded-Core</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>│   ├── meta-poky/</span></div></div><div><div><div>14</div></div><div><span><span>    </span></span><span>│   ├── meta-yocto-bsp/</span></div></div><div><div><div>15</div></div><div><span><span>    </span></span><span>│   ├── meta-skeleton/</span></div></div><div><div><div>16</div></div><div><span><span>    </span></span><span>│   ├── meta-selftest/</span></div></div><div><div><div>17</div></div><div><span><span>    </span></span><span>│   ├── documentation/</span></div></div><div><div><div>18</div></div><div><span><span>    </span></span><span>│   ├── scripts/</span></div></div><div><div><div>19</div></div><div><span><span>    </span></span><span>│   └── oe-init-build-env</span></div></div><div><div><div>20</div></div><div><span><span>    </span></span><span>├── meta-openembedded/</span></div></div><div><div><div>21</div></div><div><span><span>    </span></span><span>│   ├── meta-oe/</span></div></div><div><div><div>22</div></div><div><span><span>    </span></span><span>│   ├── meta-python/</span></div></div><div><div><div>23</div></div><div><span><span>    </span></span><span>│   ├── meta-networking/</span></div></div><div><div><div>24</div></div><div><span><span>    </span></span><span>│   ├── meta-multimedia/</span></div></div><div><div><div>25</div></div><div><span><span>    </span></span><span>│   └── ...</span></div></div><div><div><div>26</div></div><div><span><span>    </span></span><span>├── meta-vendor-bsp/</span></div></div><div><div><div>27</div></div><div><span><span>    </span></span><span>└── meta-mycompany/</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>Show more</span><span>Show less</span></div></div></section>
<section><h2>Layer tự viết<a href="#layer-tự-viết"><span>#</span></a></h2><p>Một layer tự viết cho sản phẩm, ở dạng tối thiểu mà đủ dùng, có hình hài như sau:</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>meta-mycompany/</span></div></div><div><div><div>2</div></div><div><span>├── conf/</span></div></div><div><div><div>3</div></div><div><span>│   ├── layer.conf</span></div></div><div><div><div>4</div></div><div><span>│   ├── machine/</span></div></div><div><div><div>5</div></div><div><span>│   │   └── myboard.conf</span></div></div><div><div><div>6</div></div><div><span>│   └── distro/</span></div></div><div><div><div>7</div></div><div><span>│       └── mydistro.conf</span></div></div><div><div><div>8</div></div><div><span>├── recipes-bsp/</span></div></div><div><div><div>9</div></div><div><span>│   └── u-boot/</span></div></div><div><div><div>10</div></div><div><span>│       ├── u-boot_%.bbappend</span></div></div><div><div><div>11</div></div><div><span>│       └── files/0001-add-myboard-support.patch</span></div></div><div><div><div>12</div></div><div><span>├── recipes-kernel/</span></div></div><div><div><div>13</div></div><div><span>│   └── linux/</span></div></div><div><div><div>14</div></div><div><span>│       ├── linux-vendor_%.bbappend</span></div></div><div><div><div>15</div></div><div><span>│       └── files/</span></div></div><div><div><div>16</div></div><div><span>│           ├── myboard.dts</span></div></div><div><div><div>17</div></div><div><span>│           └── enable-feature.cfg</span></div></div><div><div><div>18</div></div><div><span>├── recipes-apps/</span></div></div><div><div><div>19</div></div><div><span>│   └── hello/</span></div></div><div><div><div>20</div></div><div><span>│       ├── hello_1.0.bb</span></div></div><div><div><div>21</div></div><div><span>│       └── files/hello.c</span></div></div><div><div><div>22</div></div><div><span>├── recipes-core/</span></div></div><div><div><div>23</div></div><div><span>│   └── images/</span></div></div><div><div><div>24</div></div><div><span>│       └── myimage.bb</span></div></div><div><div><div>25</div></div><div><span>├── COPYING.MIT</span></div></div><div><div><div>26</div></div><div><span>└── README</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>Show more</span><span>Show less</span></div></div><p>Trái tim của layer là <code>conf/layer.conf</code>. File này cho BitBake biết layer tìm recipe ở đâu, có độ ưu tiên bao nhiêu khi đè lên layer khác, phụ thuộc layer nào, và tương thích với release Yocto nào:</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>BBPATH</span><span> </span><span>.=</span><span> </span><span>":</span><span><span>${</span><span>LAYERDIR</span><span>}</span></span><span>"</span></div></div><div><div><div>2</div></div><div>
</div></div><div><div><div>3</div></div><div><span>BBFILES</span><span> </span><span>+=</span><span> </span><span>"</span><span><span>${</span><span>LAYERDIR</span><span>}</span></span><span>/recipes-*/*/*.bb </span><span>\</span></div></div><div><div><div>4</div></div><div><span><span>            </span></span><span><span>${</span><span>LAYERDIR</span><span>}</span></span><span>/recipes-*/*/*.bbappend"</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>BBFILE_COLLECTIONS</span><span> </span><span>+=</span><span> </span><span>"mycompany"</span></div></div><div><div><div>7</div></div><div><span>BBFILE_PATTERN_mycompany</span><span> </span><span>=</span><span> </span><span>"^</span><span><span>${</span><span>LAYERDIR</span><span>}</span></span><span>/"</span></div></div><div><div><div>8</div></div><div><span>BBFILE_PRIORITY_mycompany</span><span> </span><span>=</span><span> </span><span>"10"</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>LAYERDEPENDS_mycompany</span><span> </span><span>=</span><span> </span><span>"core openembedded-layer"</span></div></div><div><div><div>11</div></div><div><span>LAYERSERIES_COMPAT_mycompany</span><span> </span><span>=</span><span> </span><span>"scarthgap"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>Một recipe đơn giản<a href="#một-recipe-đơn-giản"><span>#</span></a></h2><p>Một recipe đơn giản nhất, biên dịch một file C rồi cài vào <code>/usr/bin</code>, đọc lên gần như tự giải thích. Riêng dòng checksum license là thứ Yocto bắt buộc, để quá trình kiểm tra giấy phép không bị bỏ qua:</p><div><div><div><figure><figcaption><span></span><span>Terminal window</span><span>Show more</span><span>Show less</span></figcaption><pre><code><div><div><div>1</div></div><div><span>SUMMARY</span><span> </span><span>=</span><span> </span><span>"Hello world example"</span></div></div><div><div><div>2</div></div><div><span>LICENSE</span><span> </span><span>=</span><span> </span><span>"MIT"</span></div></div><div><div><div>3</div></div><div><span>LIC_FILES_CHKSUM</span><span> </span><span>=</span><span> </span><span>"file://</span><span><span>${</span><span>COMMON_LICENSE_DIR</span><span>}</span></span><span>/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>SRC_URI</span><span> </span><span>=</span><span> </span><span>"file://hello.c"</span></div></div><div><div><div>6</div></div><div><span>S</span><span> </span><span>=</span><span> </span><span>"</span><span><span>${</span><span>WORKDIR</span><span>}</span></span><span>"</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>do_compile</span><span>() {</span></div></div><div><div><div>9</div></div><div><span><span>    </span></span><span><span>${</span><span>CC</span><span>}</span></span><span> </span><span><span>${</span><span>CFLAGS</span><span>}</span></span><span> </span><span><span>${</span><span>LDFLAGS</span><span>}</span></span><span> </span><span>hello.c</span><span> </span><span>-o</span><span> </span><span>hello</span></div></div><div><div><div>10</div></div><div><span>}</span></div></div><div><div><div>11</div></div><div>
</div></div><div><div><div>12</div></div><div><span>do_install</span><span>() {</span></div></div><div><div><div>13</div></div><div><span>    </span><span>install</span><span> </span><span>-d</span><span> </span><span><span>${</span><span>D</span><span>}${</span><span>bindir</span><span>}</span></span></div></div><div><div><div>14</div></div><div><span>    </span><span>install</span><span> </span><span>-m</span><span> </span><span>0755</span><span> </span><span>hello</span><span> </span><span><span>${</span><span>D</span><span>}${</span><span>bindir</span><span>}</span></span></div></div><div><div><div>15</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>Show more</span><span>Show less</span></div></div></section>
<section><h2>Hai file cấu hình<a href="#hai-file-cấu-hình"><span>#</span></a></h2><p>Hai file quan trọng nhất cả dự án. File thứ nhất, <code>bblayers.conf</code>, quyết định những layer nào tham gia bữa tiệc:</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>BBLAYERS</span><span> </span><span>?=</span><span> </span><span>" </span><span>\</span></div></div><div><div><div>2</div></div><div><span><span>  </span></span><span><span>${</span><span>TOPDIR</span><span>}</span></span><span>/../sources/poky/meta </span><span>\</span></div></div><div><div><div>3</div></div><div><span><span>  </span></span><span><span>${</span><span>TOPDIR</span><span>}</span></span><span>/../sources/poky/meta-poky </span><span>\</span></div></div><div><div><div>4</div></div><div><span><span>  </span></span><span><span>${</span><span>TOPDIR</span><span>}</span></span><span>/../sources/meta-openembedded/meta-oe </span><span>\</span></div></div><div><div><div>5</div></div><div><span><span>  </span></span><span><span>${</span><span>TOPDIR</span><span>}</span></span><span>/../sources/meta-vendor-bsp </span><span>\</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span><span>${</span><span>TOPDIR</span><span>}</span></span><span>/../sources/meta-mycompany </span><span>\</span></div></div><div><div><div>7</div></div><div><span>"</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>File thứ hai, <code>local.conf</code>, quyết định build cho máy nào, theo distro nào, và dùng tài nguyên ra sao:</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>MACHINE</span><span> </span><span>?=</span><span> </span><span>"myboard"</span></div></div><div><div><div>2</div></div><div><span>DISTRO</span><span> </span><span>?=</span><span> </span><span>"poky"</span></div></div><div><div><div>3</div></div><div><span>PACKAGE_CLASSES</span><span> </span><span>?=</span><span> </span><span>"package_ipk"</span></div></div><div><div><div>4</div></div><div>
</div></div><div><div><div>5</div></div><div><span>BB_NUMBER_THREADS</span><span> </span><span>?=</span><span> </span><span>"8"</span></div></div><div><div><div>6</div></div><div><span>PARALLEL_MAKE</span><span> </span><span>?=</span><span> </span><span>"-j 8"</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>DL_DIR</span><span> </span><span>?=</span><span> </span><span>"</span><span><span>${</span><span>TOPDIR</span><span>}</span></span><span>/../downloads"</span></div></div><div><div><div>9</div></div><div><span>SSTATE_DIR</span><span> </span><span>?=</span><span> </span><span>"</span><span><span>${</span><span>TOPDIR</span><span>}</span></span><span>/../sstate-cache"</span></div></div></code></pre><div><div></div><div></div></div></figure></div></section>
<section><h2>Sau lần build đầu tiên<a href="#sau-lần-build-đầu-tiên"><span>#</span></a></h2><p>Sau khi chạy <code>bitbake myimage</code> lần đầu, cây thư mục phình ra thêm những phần sau:</p><figcaption>Bảng 2: Thư mục sinh ra sau lần build đầu tiên</figcaption>

<table><thead><tr><th>Đường dẫn</th><th>Chứa gì</th></tr></thead><tbody><tr><td><code>downloads/</code></td><td>Tarball và git mirror của mọi source đã fetch</td></tr><tr><td><code>sstate-cache/</code></td><td>Kết quả từng task đã đóng gói, để lần build sau tái sử dụng</td></tr><tr><td><code>build/tmp/work/&lt;arch&gt;/&lt;recipe&gt;/&lt;version&gt;/</code></td><td>Không gian làm việc riêng của từng recipe: source đã giải nén, thư mục build, <code>image/</code> (kết quả <code>do_install</code>), <code>packages-split/</code></td></tr><tr><td><code>build/tmp/work/.../temp/</code></td><td><code>log.do_&lt;task&gt;</code> và <code>run.do_&lt;task&gt;</code>, nơi đầu tiên cần mở khi một task lỗi</td></tr><tr><td><code>build/tmp/deploy/images/&lt;machine&gt;/</code></td><td>Sản phẩm cuối: kernel, device tree, bootloader, rootfs image</td></tr><tr><td><code>build/tmp/deploy/ipk/</code> (hoặc <code>rpm/</code>, <code>deb/</code>)</td><td>Các gói phần mềm riêng lẻ</td></tr><tr><td><code>build/tmp/sysroots-components/</code></td><td>Header và thư viện mỗi recipe công bố cho các recipe phụ thuộc vào nó</td></tr><tr><td><code>build/cache/</code></td><td>Cache kết quả parse metadata của BitBake</td></tr></tbody></table></section>]]></content>
    </entry>
    <entry>
      <id>https://swordart.blog/posts/flash-rb5/</id>
      <title type="text">Flashing the RB5: Hands-on</title>
      <published>2026-09-23T00:00:00.000Z</published>
      <updated>2026-09-23T00:00:00.000Z</updated>
      <author><name>Art</name></author>
      <link rel="alternate" href="https://swordart.blog/posts/flash-rb5/"/>
      <summary type="text">Flashing a Qualcomm Robotics RB5 board by hand, from EDL mode to a dry-run check.</summary>
      <content type="html"><![CDATA[<p>Sau khi vào được chế độ EDL mode, thì mình dùng tool <a href="/posts/qdl/">qdl</a> của Qualcomm cung cấp để tiến hành flash trên device.</p>
<section><h2>Luồng hoạt động của qdl<a href="#luồng-hoạt-động-của-qdl"><span>#</span></a></h2><p></p><figure><img alt="Hình 1: Luồng hoạt động của qdl." loading="lazy" width="921" height="624" src="/_astro/pbl-rom.Cyl4o6cy_2dqnBg.webp" /><figcaption>Hình 1: Luồng hoạt động của qdl.</figcaption></figure><p></p><p>Như trên hình 1, luồng hoạt động hình dung như một cuộc chuyển chặng gồm 2 phần. Chặng đầu, PBL trong ROM nói chuyện bằng Sahara để nhận firehose programmer. Chặng sau, programmer ấy chạy trên chip, nói bằng Firehose (dạng XML), đọc các file rawprogram/patch và ghi từng phân vùng.</p></section>
<section><h2>Chạy thử bằng dry-run<a href="#chạy-thử-bằng-dry-run"><span>#</span></a></h2><p>Mình có thể chạy câu lệnh dry-run để kiểm tra trước, chạy thực tế thì bạn bỏ dry-run:</p><div><figure><figcaption><span></span><span>Terminal window</span></figcaption><pre><code><div><div><div>1</div></div><div><span>qdl</span><span> </span><span>--dry-run</span><span> </span><span>prog_firehose_ddr.elf</span><span> </span><span>rawprogram</span><span>*</span><span>.xml</span><span> </span><span>patch</span><span>*</span><span>.xml</span></div></div></code></pre><div><div></div><div></div></div></figure></div><p>Kết quả của câu lệnh dry-run sẽ check được trước lỗi trước khi flash lên device:</p><div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>This is a dry-run execution of QDL. No actual flashing has been performed</span></div></div><div><div><div>2</div></div><div><span>waiting for programmer...</span></div></div><div><div><div>3</div></div><div><span>flashed "persist" successfully</span></div></div><div><div><div>4</div></div><div><span>...</span></div></div><div><div><div>5</div></div><div><span>flashed "persist" successfully</span></div></div><div><div><div>6</div></div><div><span>flashed "system_a" successfully</span></div></div><div><div><div>7</div></div><div><span>...</span></div></div><div><div><div>8</div></div><div><span>flashed "system_a" successfully</span></div></div><div><div><div>9</div></div><div><span>flashed "system_b" successfully</span></div></div><div><div><div>10</div></div><div><span>...</span></div></div><div><div><div>11</div></div><div><span>flashed "system_b" successfully</span></div></div><div><div><div>12</div></div><div><span>flashed "userdata" successfully</span></div></div><div><div><div>13</div></div><div><span>...</span></div></div><div><div><div>14</div></div><div><span>flashed "userdata" successfully</span></div></div><div><div><div>15</div></div><div><span>flashed "PrimaryGPT" successfully</span></div></div><div><div><div>16</div></div><div><span>flashed "BackupGPT" successfully</span></div></div><div><div><div>17</div></div><div><span>flashed "xbl_a" successfully</span></div></div><div><div><div>18</div></div><div><span>flashed "xbl_config_a" successfully</span></div></div><div><div><div>19</div></div><div><span>flashed "PrimaryGPT" successfully</span></div></div><div><div><div>20</div></div><div><span>flashed "BackupGPT" successfully</span></div></div><div><div><div>21</div></div><div><span>flashed "xbl_b" successfully</span></div></div><div><div><div>22</div></div><div><span>flashed "xbl_config_b" successfully</span></div></div><div><div><div>23</div></div><div><span>flashed "PrimaryGPT" successfully</span></div></div><div><div><div>24</div></div><div><span>flashed "BackupGPT" successfully</span></div></div><div><div><div>25</div></div><div><span>flashed "ddr" successfully</span></div></div><div><div><div>26</div></div><div><span>flashed "PrimaryGPT" successfully</span></div></div><div><div><div>27</div></div><div><span>flashed "BackupGPT" successfully</span></div></div><div><div><div>28</div></div><div><span>flashed "aop_a" successfully</span></div></div><div><div><div>29</div></div><div><span>flashed "tz_a" successfully</span></div></div><div><div><div>30</div></div><div><span>flashed "hyp_a" successfully</span></div></div><div><div><div>31</div></div><div><span>flashed "bluetooth_a" successfully</span></div></div><div><div><div>32</div></div><div><span>flashed "abl_a" successfully</span></div></div><div><div><div>33</div></div><div><span>flashed "dsp_a" successfully</span></div></div><div><div><div>34</div></div><div><span>flashed "keymaster_a" successfully</span></div></div><div><div><div>35</div></div><div><span>flashed "boot_a" successfully</span></div></div><div><div><div>36</div></div><div><span>flashed "cmnlib_a" successfully</span></div></div><div><div><div>37</div></div><div><span>flashed "cmnlib64_a" successfully</span></div></div><div><div><div>38</div></div><div><span>flashed "devcfg_a" successfully</span></div></div><div><div><div>39</div></div><div><span>flashed "qupfw_a" successfully</span></div></div><div><div><div>40</div></div><div><span>flashed "dtbo_a" successfully</span></div></div><div><div><div>41</div></div><div><span>flashed "uefisecapp_a" successfully</span></div></div><div><div><div>42</div></div><div><span>flashed "multiimgoem_a" successfully</span></div></div><div><div><div>43</div></div><div><span>flashed "featenabler_a" successfully</span></div></div><div><div><div>44</div></div><div><span>flashed "imagefv_a" successfully</span></div></div><div><div><div>45</div></div><div><span>flashed "aop_b" successfully</span></div></div><div><div><div>46</div></div><div><span>flashed "tz_b" successfully</span></div></div><div><div><div>47</div></div><div><span>flashed "hyp_b" successfully</span></div></div><div><div><div>48</div></div><div><span>flashed "bluetooth_b" successfully</span></div></div><div><div><div>49</div></div><div><span>flashed "abl_b" successfully</span></div></div><div><div><div>50</div></div><div><span>flashed "dsp_b" successfully</span></div></div><div><div><div>51</div></div><div><span>flashed "keymaster_b" successfully</span></div></div><div><div><div>52</div></div><div><span>flashed "boot_b" successfully</span></div></div><div><div><div>53</div></div><div><span>flashed "cmnlib_b" successfully</span></div></div><div><div><div>54</div></div><div><span>flashed "cmnlib64_b" successfully</span></div></div><div><div><div>55</div></div><div><span>flashed "devcfg_b" successfully</span></div></div><div><div><div>56</div></div><div><span>flashed "qupfw_b" successfully</span></div></div><div><div><div>57</div></div><div><span>flashed "dtbo_b" successfully</span></div></div><div><div><div>58</div></div><div><span>flashed "uefisecapp_b" successfully</span></div></div><div><div><div>59</div></div><div><span>flashed "multiimgoem_b" successfully</span></div></div><div><div><div>60</div></div><div><span>flashed "featenabler_b" successfully</span></div></div><div><div><div>61</div></div><div><span>flashed "imagefv_b" successfully</span></div></div><div><div><div>62</div></div><div><span>flashed "apdp" successfully</span></div></div><div><div><div>63</div></div><div><span>flashed "spunvm" successfully</span></div></div><div><div><div>64</div></div><div><span>flashed "logfs" successfully</span></div></div><div><div><div>65</div></div><div><span>flashed "PrimaryGPT" successfully</span></div></div><div><div><div>66</div></div><div><span>flashed "BackupGPT" successfully</span></div></div><div><div><div>67</div></div><div><span>flashed "PrimaryGPT" successfully</span></div></div><div><div><div>68</div></div><div><span>flashed "BackupGPT" successfully</span></div></div><div><div><div>69</div></div><div><span>78 patches applied</span></div></div><div><div><div>70</div></div><div><span>partition 1 is now bootable</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>Show more</span><span>Show less</span></div></div><p>Trong log, chỉ 4 partition là flash nhiều lệnh: <code>persist</code>, <code>system_a</code>, <code>system_b</code>, <code>userdata</code>. Vì chúng là sparse image, mỗi chunk qdl log một dòng.</p><p>Câu hỏi đặt ra: mình đã flash <strong>đủ</strong> và <strong>đúng</strong> chưa? Phần đúng thì chờ board boot lên là biết. Phần đủ thì khó hơn, vì log chỉ kể những gì qdl đã ghi, chứ không kể những gì lẽ ra phải ghi mà lại thiếu.</p></section>
<section><h2>Ghi vào UFS<a href="#ghi-vào-ufs"><span>#</span></a></h2><p>Toàn bộ số file trên đi vào UFS của board. Mô hình một chip UFS mình ghi lại trong bài <a href="/posts/ufs/">UFS: My Understanding</a>: mỗi partition nằm trên một LU riêng, nên mỗi dòng trong <code>rawprogram</code> phải nói đủ hai toạ độ, LU nào và LBA nào.</p></section>
<section><h2>Hướng tiếp theo: bring-up bằng mã nguồn mở<a href="#hướng-tiếp-theo-bring-up-bằng-mã-nguồn-mở"><span>#</span></a></h2><p>Bản build mình vừa flash đến từ SDK của hãng, muốn lấy thì phải ký NDA. Nên câu hỏi mình muốn trả lời tiếp là: <strong>muốn đưa con board này lên tới mức chạy được Linux thì cần tối thiểu những gì, và trong đó phần nào đã có sẵn dưới dạng mã nguồn mở?</strong></p><p>Tạm liệt kê từng mảnh theo thứ tự chúng xuất hiện trong một lần bring-up:</p><figcaption>Bảng 1: Các thành phần cần cho một lần bring-up và tình trạng nguồn mở</figcaption>

<table><thead><tr><th>Thành phần</th><th>Vai trò</th><th>Tình trạng</th></tr></thead><tbody><tr><td><code>qdl</code></td><td>Công cụ nạp chạy trên máy host</td><td>Mã nguồn mở: <a href="https://github.com/linux-msm/qdl" target="_blank">linux-msm/qdl</a></td></tr><tr><td>Firehose programmer (<code>prog_firehose_ddr.elf</code>)</td><td>Chạy trên chip, nhận lệnh rồi ghi vào UFS</td><td>Do hãng ký, chỉ có trong bản phát hành</td></tr><tr><td>Bootloader: XBL, TZ, ABL</td><td>Khởi động sớm và giữ chain of trust</td><td>Binary của hãng</td></tr><tr><td>Kernel</td><td>Nhân Linux cho SM8250</td><td>Mainline đã có hỗ trợ, do Linaro upstream (<a href="https://www.qualcomm.com/news/onq/2021/07/running-upstream-linux-qualcomm-robotics-rb5-platform" target="_blank">Qualcomm OnQ</a>)</td></tr><tr><td>Rootfs</td><td>Hệ thống tệp gốc</td><td>Bản dựng công khai của Linaro, xem bài <a href="/posts/rb5-linaro/">Linaro</a></td></tr><tr><td>Firmware DSP, WLAN, GPU</td><td>Nạp lúc chạy qua <code>request_firmware()</code></td><td>Chủ yếu là blob; riêng WLAN QCA6390 có trong <code>linux-firmware</code> (<code>ath11k/QCA6390/hw2.0</code>)</td></tr></tbody></table><p>Nhìn bảng thì thấy ranh giới khá rõ: từ kernel trở lên đã có đường công khai, còn mọi thứ chạy <strong>trước</strong> kernel, tức programmer và bootloader, vẫn phải lấy từ bản phát hành của hãng.</p><p>Nên hướng thử tiếp theo của mình là đi đúng theo ranh giới đó: giữ nguyên bootloader của hãng, còn boot image và rootfs thì dựng từ nguồn mở, rồi xem cái gì gãy và thiếu firmware nào.</p><p>Comming soon.</p></section>]]></content>
    </entry>
    <entry>
      <id>https://swordart.blog/posts/ufs/</id>
      <title type="text">UFS: My Understanding</title>
      <published>2026-09-23T00:00:00.000Z</published>
      <updated>2026-09-23T00:00:00.000Z</updated>
      <author><name>Art</name></author>
      <link rel="alternate" href="https://swordart.blog/posts/ufs/"/>
      <summary type="text">How I understand UFS, the storage chip on Qualcomm boards.</summary>
      <content type="html"><![CDATA[<p><strong>UFS</strong> là một chip nhớ flash tự quản lý chính nó và nói chuyện với SoC theo kiểu một ổ đĩa SCSI. Bên trong là NAND flash cộng với một bộ điều khiển. Bộ điều khiển chia khối flash đó thành nhiều ổ đĩa logic độc lập, gọi là <strong>LU (Logical Unit)</strong>, rồi đón lệnh từ SoC qua một đường truyền nối tiếp tốc độ cao.</p>
<p>Mọi lệnh gửi xuống đều mang sẵn địa chỉ “gửi tới LU nào”. Nhờ vậy SoC làm việc với nhiều LU cùng lúc, không phải chuyển công tắc như eMMC.</p>
<p></p><figure><img alt="Hình 1: Mô hình một chip UFS." loading="lazy" width="791" height="522" src="/_astro/report-luns.CN6wB8pJ_11a5zO.webp" /><figcaption>Hình 1: Mô hình một chip UFS.</figcaption></figure><p></p>
<section><h2>LU thường: nơi chứa dữ liệu<a href="#lu-thường-nơi-chứa-dữ-liệu"><span>#</span></a></h2><p>Khung bên trái của hình là các LU thường, nơi dữ liệu thực sự nằm. Chuẩn cho phép tối đa 32 LU, từ LU 0 đến LU 31, giới hạn bởi <code>bMaxNumberLU</code>. Nhà sản xuất thiết bị chỉ bật số lượng mình cần lúc provisioning.</p><p>Mỗi LU giống một ổ đĩa riêng, hay giống không gian địa chỉ riêng của một process:</p><ul>
<li>bắt đầu từ LBA 0,</li>
<li>có GPT riêng ở đầu,</li>
<li>có các phân vùng riêng,</li>
<li>không LU nào chạm được vào khối của LU khác.</li>
</ul><p>Đây là nơi <code>rawprogram</code> ghi image và <code>patch</code> sửa GPT, tức toàn bộ công việc của <a href="/posts/qdl/">qdl</a> diễn ra trong khung này.</p></section>
<section><h2>Bốn well-known LUN<a href="#bốn-well-known-lun"><span>#</span></a></h2><p>Khung bên phải là bốn <strong>well-known LUN (W-LUN)</strong>. Địa chỉ của chúng do chuẩn cố định nên ai cũng biết trước, khỏi phải dò tìm. Chúng không phải chỗ để nạp image, mà là những cánh cửa chức năng.</p><figcaption>Bảng 1: Bốn well-known LUN</figcaption>

<table><thead><tr><th>Địa chỉ</th><th>Tên</th><th>Vai trò</th></tr></thead><tbody><tr><td><code>01h</code></td><td>REPORT_LUNS</td><td>Trả lời “chip này đang có những LU nào”, kế thừa từ chuẩn SCSI</td></tr><tr><td><code>30h</code></td><td>BOOT</td><td>Bí danh trỏ tới LU chứa bootloader đang được bật</td></tr><tr><td><code>44h</code></td><td>RPMB</td><td>Vùng nhớ nhỏ, chỉ ghi được khi có chữ ký đúng</td></tr><tr><td><code>50h</code></td><td>UFS Device</td><td>Nhận lệnh cấp toàn chip: đổi trạng thái nguồn, reset, cấu hình</td></tr></tbody></table><p><strong>BOOT (<code>30h</code>)</strong> không có vùng nhớ riêng. Mũi tên trong hình trỏ sang LU 1 chính là mối quan hệ này: khi ROM trong SoC đọc BOOT lúc khởi động, thực chất nó đang đọc LU 1. Muốn chuyển sang slot B, chỉ cần đổi thuộc tính để mũi tên trỏ sang LU khác.</p><p><strong>RPMB (<code>44h</code>)</strong> là W-LUN duy nhất có vùng nhớ thật. Muốn ghi vào đó phải kèm chữ ký đúng từ khoá bí mật mà TrustZone giữ. Vì vậy RPMB dùng để cất những thứ không được phép sửa lén, ví dụ bộ đếm chống hạ cấp firmware.</p></section>]]></content>
    </entry>
    <entry>
      <id>https://swordart.blog/posts/qualcomm-dsp-driver-exploit/</id>
      <title type="text">The Qualcomm DSP Driver - Unexpectedly Excavating an Exploit</title>
      <published>2026-09-15T00:00:00.000Z</published>
      <updated>2026-09-15T00:00:00.000Z</updated>
      <author><name>Art</name></author>
      <link rel="alternate" href="https://swordart.blog/posts/qualcomm-dsp-driver-exploit/"/>
      <summary type="text">Notes on Google Project Zero&apos;s analysis of an in-the-wild exploit in Qualcomm&apos;s adsprpc (FastRPC) DSP driver, CVE-2024-43047.</summary>
      <content type="html"><![CDATA[<p>Comming soon.</p>
<p>Ref:<br />
[1] <a href="https://projectzero.google/2024/12/qualcomm-dsp-driver-unexpectedly-excavating-exploit.html" target="_blank">https://projectzero.google/2024/12/qualcomm-dsp-driver-unexpectedly-excavating-exploit.html</a><br />
[2] <a href="https://docs.qualcomm.com/securitybulletin/october-2024-bulletin.html" target="_blank">https://docs.qualcomm.com/securitybulletin/october-2024-bulletin.html</a></p>]]></content>
    </entry>
    <entry>
      <id>https://swordart.blog/posts/file-descriptor/</id>
      <title type="text">File descriptor</title>
      <published>2026-09-14T00:00:00.000Z</published>
      <updated>2026-09-14T00:00:00.000Z</updated>
      <author><name>Art</name></author>
      <link rel="alternate" href="https://swordart.blog/posts/file-descriptor/"/>
      <summary type="text">What is a file descriptor: the per-process fd table, the shared struct file, and the inode underneath.</summary>
      <content type="html"><![CDATA[<blockquote><p>How does the system keep track of the connection between the string filename <code>argv[i]</code> passed to <code>open()</code>, and the resulting integer file descriptor <code>fd</code>? What does the integer file descriptor number refer to?</p></blockquote>
<p>Câu hỏi này bắt đầu xoay quanh vào implementation ở <code>user/cat.c</code>. Nếu nhìn vào code xử lý của cat</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>if</span><span>((fd </span><span>=</span><span> </span><span>open</span><span>(</span><span>argv</span><span><span>[i]</span><span>,</span><span> </span></span><span>0</span><span>)) </span><span>&lt;</span><span> </span><span>0</span><span>){ ... }</span></div></div><div><div><div>2</div></div><div><span>cat</span><span><span>(</span><span>fd</span><span>);</span></span></div></div><div><div><div>3</div></div><div><span>close</span><span><span>(</span><span>fd</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>Từ các đoạn logic xử lý chính của cat thì có 1 câu hỏi? nếu mình truyền vào 1 string “README” thì làm sao kernel xử lý trả về là 1 interger fd, và mọi câu lệnh xử lý sau đó là số nguyên interger fd chứ không phải là chuỗi README. Nếu vậy thì câu hỏi thật sự cần quan tâm ở đầy là kernel làm sao có thể xử lý được mọi file mà chỉ cần thông qua 1 interger fd là đủ.</p>
<p>Ví dụ, sau khi thực hiện lệnh <code>cat README</code> trên user, thì systemcall <code>open</code> được gọi ra để xử lý. Đầu tiên nó lấy được 2 giá trị thông số là mode permission và chuỗi string README thông qua</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>argint</span><span>(</span><span>1</span><span><span>,</span><span> </span></span><span>&amp;</span><span><span>omode</span><span>);</span></span></div></div><div><div><div>2</div></div><div><span>n </span><span>=</span><span> </span><span>argstr</span><span>(</span><span>0</span><span><span>,</span><span> path</span><span>,</span><span> MAXPATH</span><span>)</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>Phần logic detail làm sao có thể từ user -&gt; kernel sẽ được detail ở blog systemcall. Tư đó mình có dược permission của file và chuỗi README từ kernel.</p>
<p>Có được chuỗi “README” rồi, kernel dùng nó để tìm file thật sự nằm ở đâu:</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>if</span><span>((ip </span><span>=</span><span> </span><span>namei</span><span><span>(</span><span>path</span><span>)) </span></span><span>==</span><span> </span><span>0</span><span>){</span></div></div><div><div><div>2</div></div><div><span>  </span><span>end_op</span><span>();</span></div></div><div><div><div>3</div></div><div><span>  </span><span>return</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>4</div></div><div><span>}</span></div></div><div><div><div>5</div></div><div><span>ilock</span><span><span>(</span><span>ip</span><span>);</span></span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>Ở phần này chỉ cần biết <code>namei</code> nhận vào một chuỗi tên và trả về <code>ip</code>, một con trỏ tới <code>struct inode</code> đại diện cho file đó trong kernel. Cách nó tìm ra inode sẽ có bài riêng. Điều quan trọng là đây là lần duy nhất chuỗi “README” được dùng. Từ đây trở đi, kernel chỉ làm việc với con trỏ <code>ip</code>.</p>
<p>Câu hỏi được chuyển đổi từ README sang fd thành: con trỏ <code>ip</code> sang fd?</p>
<p>Kernel không đưa thẳng <code>ip</code> cho process. Nó bọc <code>ip</code> trong một cấu trúc khác, <code>struct file</code> (<code>kernel/file.h</code>):</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> file {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>enum</span><span> { FD_NONE, FD_PIPE, FD_INODE, FD_DEVICE } type;</span></div></div><div><div><div>3</div></div><div><span>  </span><span>int</span><span> ref;</span><span> // reference count</span></div></div><div><div><div>4</div></div><div><span>  </span><span>char</span><span> readable;</span></div></div><div><div><div>5</div></div><div><span>  </span><span>char</span><span> writable;</span></div></div><div><div><div>6</div></div><div><span>  </span><span>struct</span><span> pipe </span><span>*</span><span>pipe;</span><span> // FD_PIPE</span></div></div><div><div><div>7</div></div><div><span>  </span><span>struct</span><span> inode </span><span>*</span><span>ip;</span><span>  // FD_INODE and FD_DEVICE</span></div></div><div><div><div>8</div></div><div><span>  </span><span>uint</span><span> off;</span><span>          // FD_INODE</span></div></div><div><div><div>9</div></div><div><span>  </span><span>short</span><span> major;</span><span>       // FD_DEVICE</span></div></div><div><div><div>10</div></div><div><span>};</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>Inode mô tả file. <code>struct file</code> mô tả một lần mở file: đang đọc tới byte thứ mấy (<code>off</code>), được đọc hay ghi (<code>readable</code>, <code>writable</code>). Nếu hai process cùng mở README, cả hai dùng chung một inode nhưng mỗi bên có <code>struct file</code> riêng, nên offset của bên này không ảnh hưởng bên kia.</p>
<p>Để ý trường <code>type</code>: một <code>struct file</code> không nhất thiết là file trên đĩa. Nó có thể là pipe (<code>FD_PIPE</code>) hoặc thiết bị như console (<code>FD_DEVICE</code>). Đây chính là lý do một fd có thể đại diện cho “mọi thứ”.</p>
<p>Tất cả <code>struct file</code> của toàn hệ thống nằm trong một mảng global <code>ftable</code> (<code>kernel/file.c</code>):</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> {</span></div></div><div><div><div>2</div></div><div><span>  </span><span>struct</span><span> spinlock lock;</span></div></div><div><div><div>3</div></div><div><span>  </span><span>struct</span><span> file </span><span>file</span><span>[NFILE];</span><span>   // NFILE = 100</span></div></div><div><div><div>4</div></div><div><span>} ftable;</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p><code>sys_open</code> gọi <code>filealloc()</code> để lấy một ô trống (<code>ref == 0</code>) trong mảng này:</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> file</span><span>*</span></div></div><div><div><div>2</div></div><div><span>filealloc</span><span>(</span><span>void</span><span>)</span></div></div><div><div><div>3</div></div><div><span>{</span></div></div><div><div><div>4</div></div><div><span>  </span><span>struct</span><span> file </span><span>*</span><span>f;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>  </span><span>acquire</span><span>(</span><span>&amp;</span><span>ftable</span><span>.</span><span>lock</span><span>);</span></div></div><div><div><div>7</div></div><div><span>  </span><span>for</span><span>(f </span><span>=</span><span> </span><span>ftable</span><span>.</span><span>file</span><span>; f </span><span>&lt;</span><span> </span><span>ftable</span><span>.</span><span>file</span><span> </span><span>+</span><span> NFILE; f</span><span>++</span><span>){</span></div></div><div><div><div>8</div></div><div><span>    </span><span>if</span><span>(</span><span>f</span><span>-&gt;</span><span>ref</span><span> </span><span>==</span><span> </span><span>0</span><span>){</span></div></div><div><div><div>9</div></div><div><span>      </span><span>f</span><span>-&gt;</span><span>ref</span><span> </span><span>=</span><span> </span><span>1</span><span>;</span></div></div><div><div><div>10</div></div><div><span>      </span><span>release</span><span>(</span><span>&amp;</span><span>ftable</span><span>.</span><span>lock</span><span>);</span></div></div><div><div><div>11</div></div><div><span>      </span><span>return</span><span> f;</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>14</div></div><div><span>  </span><span>release</span><span>(</span><span>&amp;</span><span>ftable</span><span>.</span><span>lock</span><span>);</span></div></div><div><div><div>15</div></div><div><span>  </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>16</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>Show more</span><span>Show less</span></div></div>
<p>Mỗi process có một bảng file đang mở của riêng nó, trong <code>struct proc</code> (<code>kernel/proc.h</code>):</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>struct</span><span> file </span><span>*</span><span>ofile</span><span>[NOFILE];</span><span>  // Open files, NOFILE = 16</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p><code>fdalloc</code> tìm ô trống đầu tiên trong mảng này, đặt con trỏ <code>f</code> vào và trả về chỉ số của ô đó:</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>static</span><span> </span><span>int</span></div></div><div><div><div>2</div></div><div><span>fdalloc</span><span>(</span><span>struct</span><span> file </span><span>*</span><span><span>f</span><span>)</span></span></div></div><div><div><div>3</div></div><div><span>{</span></div></div><div><div><div>4</div></div><div><span>  </span><span>int</span><span> fd;</span></div></div><div><div><div>5</div></div><div><span>  </span><span>struct</span><span> proc </span><span>*</span><span>p </span><span>=</span><span> </span><span>myproc</span><span>();</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>  </span><span>for</span><span>(fd </span><span>=</span><span> </span><span>0</span><span>; fd </span><span>&lt;</span><span> NOFILE; fd</span><span>++</span><span>){</span></div></div><div><div><div>8</div></div><div><span>    </span><span>if</span><span>(</span><span>p</span><span>-&gt;</span><span>ofile</span><span>[fd] </span><span>==</span><span> </span><span>0</span><span>){</span></div></div><div><div><div>9</div></div><div><span>      </span><span>p</span><span>-&gt;</span><span>ofile</span><span>[fd] </span><span>=</span><span> f;</span></div></div><div><div><div>10</div></div><div><span>      </span><span>return</span><span> fd;</span></div></div><div><div><div>11</div></div><div><span><span>    </span></span><span>}</span></div></div><div><div><div>12</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>13</div></div><div><span>  </span><span>return</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>14</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>Vậy là có câu trả lời: file descriptor chỉ là một chỉ số trong mảng <code>p-&gt;ofile[]</code> của process. Không có gì bí ẩn cả.</p>
<p>Với cat, ô 0, 1, 2 đã được dùng cho stdin, stdout, stderr (console), nên README thường nhận <code>fd = 3</code>.</p>
<p>Phần còn lại của <code>sys_open</code> điền thông tin vào <code>struct file</code> vừa cấp:</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>f</span><span>-&gt;</span><span>type </span><span>=</span><span> FD_INODE;</span></div></div><div><div><div>2</div></div><div><span>f</span><span>-&gt;</span><span>off </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>3</div></div><div><span>f</span><span>-&gt;</span><span>ip </span><span>=</span><span> ip;</span></div></div><div><div><div>4</div></div><div><span>f</span><span>-&gt;</span><span>readable </span><span>=</span><span> </span><span>!</span><span>(omode </span><span>&amp;</span><span> O_WRONLY);</span></div></div><div><div><div>5</div></div><div><span>f</span><span>-&gt;</span><span>writable </span><span>=</span><span> (omode </span><span>&amp;</span><span> O_WRONLY) </span><span>||</span><span> (omode </span><span>&amp;</span><span> O_RDWR);</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>iunlock</span><span><span>(</span><span>ip</span><span>);</span></span></div></div><div><div><div>8</div></div><div><span>end_op</span><span>();</span></div></div><div><div><div>9</div></div><div>
</div></div><div><div><div>10</div></div><div><span>return</span><span> fd;</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>User chỉ cầm số 3. Mọi thông tin thật (file nào, đọc tới đâu, có quyền gì) đều nằm trong kernel, và số 3 là chìa khóa để tìm lại chúng.</p>
<p>Hàm <code>cat()</code> trong <code>user/cat.c</code> gọi <code>read(fd, buf, sizeof(buf))</code>. Trong kernel, <code>sys_read</code> không cần biết tên file. Nó chỉ cần đổi fd ngược lại thành <code>struct file</code> qua <code>argfd</code>:</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>static</span><span> </span><span>int</span></div></div><div><div><div>2</div></div><div><span>argfd</span><span>(</span><span>int</span><span><span> </span><span>n</span><span>,</span><span> </span></span><span>int</span><span> </span><span>*</span><span><span>pfd</span><span>,</span><span> </span></span><span>struct</span><span> file </span><span>**</span><span><span>pf</span><span>)</span></span></div></div><div><div><div>3</div></div><div><span>{</span></div></div><div><div><div>4</div></div><div><span>  </span><span>int</span><span> fd;</span></div></div><div><div><div>5</div></div><div><span>  </span><span>struct</span><span> file </span><span>*</span><span>f;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>  </span><span>argint</span><span>(n, </span><span>&amp;</span><span>fd);</span></div></div><div><div><div>8</div></div><div><span>  </span><span>if</span><span>(fd </span><span>&lt;</span><span> </span><span>0</span><span> </span><span>||</span><span> fd </span><span>&gt;=</span><span> NOFILE </span><span>||</span><span> (f</span><span>=</span><span>myproc</span><span>()-&gt;</span><span>ofile</span><span>[fd]) </span><span>==</span><span> </span><span>0</span><span>)</span></div></div><div><div><div>9</div></div><div><span>    </span><span>return</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>10</div></div><div><span>  </span><span>if</span><span>(pfd)</span></div></div><div><div><div>11</div></div><div><span>    </span><span>*</span><span>pfd </span><span>=</span><span> fd;</span></div></div><div><div><div>12</div></div><div><span>  </span><span>if</span><span>(pf)</span></div></div><div><div><div>13</div></div><div><span>    </span><span>*</span><span>pf </span><span>=</span><span> f;</span></div></div><div><div><div>14</div></div><div><span>  </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>15</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>Show more</span><span>Show less</span></div></div>
<p>Chỉ là <code>myproc()-&gt;ofile[fd]</code>, kèm kiểm tra fd có hợp lệ và có đang được mở hay không. Sau đó:</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>uint64</span></div></div><div><div><div>2</div></div><div><span>sys_read</span><span>(</span><span>void</span><span>)</span></div></div><div><div><div>3</div></div><div><span>{</span></div></div><div><div><div>4</div></div><div><span>  </span><span>struct</span><span> file </span><span>*</span><span>f;</span></div></div><div><div><div>5</div></div><div><span>  </span><span>int</span><span> n;</span></div></div><div><div><div>6</div></div><div><span><span>  </span></span><span>uint64 p;</span></div></div><div><div><div>7</div></div><div>
</div></div><div><div><div>8</div></div><div><span>  </span><span>argaddr</span><span>(</span><span>1</span><span>, </span><span>&amp;</span><span>p);</span></div></div><div><div><div>9</div></div><div><span>  </span><span>argint</span><span>(</span><span>2</span><span>, </span><span>&amp;</span><span>n);</span></div></div><div><div><div>10</div></div><div><span>  </span><span>if</span><span>(</span><span>argfd</span><span>(</span><span>0</span><span>, </span><span>0</span><span>, </span><span>&amp;</span><span>f) </span><span>&lt;</span><span> </span><span>0</span><span>)</span></div></div><div><div><div>11</div></div><div><span>    </span><span>return</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>12</div></div><div><span>  </span><span>return</span><span> </span><span>fileread</span><span>(f, p, n);</span></div></div><div><div><div>13</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p><code>fileread</code> nhìn vào <code>f-&gt;type</code> để quyết định đọc từ đâu:</p>
<div><div><div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>int</span></div></div><div><div><div>2</div></div><div><span>fileread</span><span>(</span><span>struct</span><span> file </span><span>*</span><span><span>f</span><span>,</span><span> uint64 </span><span>addr</span><span>,</span><span> </span></span><span>int</span><span><span> </span><span>n</span><span>)</span></span></div></div><div><div><div>3</div></div><div><span>{</span></div></div><div><div><div>4</div></div><div><span>  </span><span>int</span><span> r </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>5</div></div><div>
</div></div><div><div><div>6</div></div><div><span>  </span><span>if</span><span>(</span><span>f</span><span>-&gt;</span><span>readable</span><span> </span><span>==</span><span> </span><span>0</span><span>)</span></div></div><div><div><div>7</div></div><div><span>    </span><span>return</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>8</div></div><div>
</div></div><div><div><div>9</div></div><div><span>  </span><span>if</span><span>(</span><span>f</span><span>-&gt;</span><span>type</span><span> </span><span>==</span><span> FD_PIPE){</span></div></div><div><div><div>10</div></div><div><span><span>    </span></span><span>r </span><span>=</span><span> </span><span>piperead</span><span>(</span><span>f</span><span>-&gt;</span><span>pipe</span><span>, addr, n);</span></div></div><div><div><div>11</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> </span><span>if</span><span>(</span><span>f</span><span>-&gt;</span><span>type</span><span> </span><span>==</span><span> FD_DEVICE){</span></div></div><div><div><div>12</div></div><div><span><span>    </span></span><span>...</span></div></div><div><div><div>13</div></div><div><span><span>    </span></span><span>r </span><span>=</span><span> </span><span>devsw</span><span>[</span><span>f</span><span>-&gt;</span><span>major</span><span>].</span><span>read</span><span>(</span><span>1</span><span>, addr, n);</span></div></div><div><div><div>14</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> </span><span>if</span><span>(</span><span>f</span><span>-&gt;</span><span>type</span><span> </span><span>==</span><span> FD_INODE){</span></div></div><div><div><div>15</div></div><div><span>    </span><span>ilock</span><span>(</span><span>f</span><span>-&gt;</span><span>ip</span><span>);</span></div></div><div><div><div>16</div></div><div><span>    </span><span>if</span><span>((r </span><span>=</span><span> </span><span>readi</span><span>(</span><span>f</span><span>-&gt;</span><span>ip</span><span>, </span><span>1</span><span>, addr, </span><span>f</span><span>-&gt;</span><span>off</span><span>, n)) </span><span>&gt;</span><span> </span><span>0</span><span>)</span></div></div><div><div><div>17</div></div><div><span>      </span><span>f</span><span>-&gt;</span><span>off</span><span> </span><span>+=</span><span> r;</span></div></div><div><div><div>18</div></div><div><span>    </span><span>iunlock</span><span>(</span><span>f</span><span>-&gt;</span><span>ip</span><span>);</span></div></div><div><div><div>19</div></div><div><span><span>  </span></span><span>} </span><span>else</span><span> {</span></div></div><div><div><div>20</div></div><div><span>    </span><span>panic</span><span>(</span><span>"fileread"</span><span>);</span></div></div><div><div><div>21</div></div><div><span><span>  </span></span><span>}</span></div></div><div><div><div>22</div></div><div>
</div></div><div><div><div>23</div></div><div><span>  </span><span>return</span><span> r;</span></div></div><div><div><div>24</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure><div></div></div><span>Show more</span><span>Show less</span></div></div>
<p>Đây là lời giải cho câu hỏi “làm sao chỉ với một số nguyên mà xử lý được mọi thứ”:</p>
<ul>
<li><code>read(3, ...)</code> với README đi vào nhánh <code>FD_INODE</code>, đọc từ đĩa rồi tăng <code>f-&gt;off</code>. Lần read sau sẽ đọc tiếp từ chỗ cũ, dù user không hề truyền offset.</li>
<li><code>read(0, ...)</code> từ console đi vào nhánh <code>FD_DEVICE</code>.</li>
<li>read từ đầu đọc của pipe đi vào nhánh <code>FD_PIPE</code>.</li>
</ul>
<p>Code của cat giống hệt nhau trong cả ba trường hợp. Sự khác biệt được giấu trong <code>struct file</code>. Nhờ vậy, <code>cat README</code>, <code>cat &lt; README</code> hay <code>echo hi | cat</code> đều chạy được mà cat không cần biết mình đang đọc từ đâu.</p>
<p>Cuối cùng cat gọi <code>close(fd)</code>, tức là tháo liên kết ra:</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>uint64</span></div></div><div><div><div>2</div></div><div><span>sys_close</span><span>(</span><span>void</span><span>)</span></div></div><div><div><div>3</div></div><div><span>{</span></div></div><div><div><div>4</div></div><div><span>  </span><span>int</span><span> fd;</span></div></div><div><div><div>5</div></div><div><span>  </span><span>struct</span><span> file </span><span>*</span><span>f;</span></div></div><div><div><div>6</div></div><div>
</div></div><div><div><div>7</div></div><div><span>  </span><span>if</span><span>(</span><span>argfd</span><span>(</span><span>0</span><span>, </span><span>&amp;</span><span>fd, </span><span>&amp;</span><span>f) </span><span>&lt;</span><span> </span><span>0</span><span>)</span></div></div><div><div><div>8</div></div><div><span>    </span><span>return</span><span> </span><span>-</span><span>1</span><span>;</span></div></div><div><div><div>9</div></div><div><span>  </span><span>myproc</span><span>()-&gt;</span><span>ofile</span><span>[fd] </span><span>=</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>10</div></div><div><span>  </span><span>fileclose</span><span>(f);</span></div></div><div><div><div>11</div></div><div><span>  </span><span>return</span><span> </span><span>0</span><span>;</span></div></div><div><div><div>12</div></div><div><span>}</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<ul>
<li><code>ofile[fd] = 0</code> giải phóng ô trong bảng của process. Lần open sau có thể dùng lại số 3.</li>
<li><code>fileclose(f)</code> giảm <code>f-&gt;ref</code>. Khi ref về 0, ô trong <code>ftable</code> được giải phóng và <code>iput(ff.ip)</code> trả lại tham chiếu tới inode.</li>
<li>ref cần thiết vì một <code>struct file</code> có thể được nhiều fd trỏ tới cùng lúc, ví dụ sau <code>dup()</code>, hoặc khi <code>fork()</code> copy <code>ofile[]</code> sang process con qua <code>filedup</code>. Chỉ khi không còn ai dùng thì <code>struct file</code> mới thật sự bị giải phóng.</li>
</ul>
<p>Kernel không “nhớ” liên kết giữa chuỗi “README” và số 3. Chuỗi tên chỉ dùng một lần trong <code>open</code> để tìm ra inode. Sau đó kernel tạo ra một chuỗi con trỏ:</p>
<div><figure><figcaption></figcaption><pre><code><div><div><div>1</div></div><div><span>fd (int)  →  p-&gt;ofile[fd]  →  struct file { type, off, readable, writable, ip / pipe }  →  inode / pipe / device</span></div></div></code></pre><div><div></div><div></div></div></figure></div>
<p>Và trả cho user chỉ số của mắt xích đầu tiên. Mọi syscall sau đó (<code>read</code>, <code>write</code>, <code>close</code>, <code>fstat</code>, <code>dup</code>) đều bắt đầu bằng cùng một bước: <code>myproc()-&gt;ofile[fd]</code>.</p>]]></content>
    </entry>
</feed>
