跳转至

博客

jiegec.xyz 域名即将停用

最早买域名的时候,买的是 jiegec.xyz,后来发现了更好的域名,于是这个老域名一直就是一个简单的网页转址。很快,它就要过期了,我也不打算续费了,大家继续用我的新域名吧。

P.S. 忽然发现我之前配的 acme.sh 没有配自动 nginx -s reload ,所以之前 ssl 证书时间凉了。

每周分享第 20 期

写到了第 20 期了!最近几期总是在咕咕。

  1. Rust 的科学计算库 https://github.com/rust-ndarray/ndarray https://github.com/jturner314/ndarray-stats
  2. Rust Crate 生态可视化 https://rfdonnelly.github.io/crate-galaxy/
  3. 在地址栏里做动画 http://matthewrayfield.com/articles/animating-urls-with-javascript-and-emojis/#%F0%9F%8C%92
  4. Ruby 3 也要加类型了 https://twitter.com/darkdimius/status/1119115657776209920
  5. verilog 的 MAC + 简易网络栈 https://github.com/alexforencich/verilog-ethernet

每周分享第 19 期

忽然想起来忘了这件事情两天。。

  1. Pock 在 Touchbar 中显示并控制 Dock https://pock.pigigaldi.com/
  2. MacBook touchbar+keyboard+touchpad Linux 驱动 https://github.com/roadrunner2/macbook12-spi-driver
  3. Kendryte K210 上的 GBA 模拟器 https://github.com/44670/mgba-k210
  4. Rust 生成 PDF 文件的库 https://github.com/J-F-Liu/lopdf
  5. 中科大用 Rust 编写 FreeRTOS 的实验 https://github.com/OSH-2019/x-rust-freertos
  6. Google 发布 Cloud Code 插件,直接在 k8s 上调试运行 https://cloud.google.com/blog/products/devops-sre/announcing-cloud-code-accelerating-cloud-native-application-development
  7. 通过 libusb 跨平台的 usbip server https://github.com/jwise/pyusbip
  8. Linux 下 BCM43602 Firmware 问题的不完美解决方法 https://bugzilla.kernel.org/show_bug.cgi?id=193121
  9. 给树莓派上电的新方法 https://youtu.be/X2vF9KAEJx8
  10. Docker 里跑 Deepin Wechat 的镜像 https://github.com/bestwu/docker-wechat.git
  11. NS Switch 的包管理器 https://switchbrew.org/wiki/Main_Page

rCore 软路由实现

最近在研究软路由在 rCore 上的实现,但限于硬件限制,目前先在虚拟机里测试。软路由大概要做这些东西:

1. 抓包,解析包里的内容
2. 查路由表,找到下一跳在哪
3. 查 ARP,知道下一跳的 MAC 地址
4. 减少 TTL,更新 IP Checksum
5. 把包发出去

第一步直接拿 smoltcp 的 Raw Socket 即可,但是目前只能抓指定 IP Protocol 的包,我用的是 ICMP,但其他的就还抓不了,需要继续改 Smoltcp 源代码。

第二步用的是之前刚修好的 treebitmap 库,它提供了路由表的查询功能,目前路由表还是写死的,之后会用已经部分实现好的 Netlink 接口读取出来。

第三步则是 ioctl 发请求,然后从 smoltcp 内部的 ARP cache 里读取。

第四步很简单,不用多说。

第五步则需要指定出端口,用了一个 index,放在一个特定的 sockaddr 中。

最后的效果就是,能双向转发 ping 通。

网络拓扑:

可以,这很玄学。

后续在想在真机上实验,但是还缺一个网卡驱动,不然就可以用神奇的办法来做这个实验了。

每周分享第 18 期

不咕不咕

  1. Alfred pwgen workflow https://github.com/deanishe/alfred-pwgen
  2. 魔改主板,在老主板上放 16GB 内存 https://readhacker.news/s/3Zty4
  3. Mesalink TLS 库的 Rust 实现 https://github.com/mesalock-linux/mesalink
  4. 实测可用 Rust 写的 STM32 应用 https://github.com/lupyuen/stm32-blue-pill-rust
  5. 开源 PCIe 核 https://github.com/enjoy-digital/litepcie

欢迎投稿。

高云 FPGA 踩坑

最近拿到了高云 FPGA GW2A-18 开发版,想在这上面做一些小工程。不过首先要配置好环境什么的。官方提供了 Linux 和 Windows 的两套工具,自然是拥抱 Linux 咯,但是由于官方适配的是 Redhat 系的操作系统,所以用 Debian 系的时候出现了若干问题,后面会谈到怎么解决的。

首先是官网下载了它的软件,大概有 IDE,综合器,布线器和 Programmer 四个工具,然后开始跑,发现缺少了 libcrypt.so.1.0.0。上网搜了一下解决方案,需要重新编译 openssl-1.0.0,于是下载并且编译了 openssl-1.0.0t 并且把 .so 的路径调好了,这时候就可以打开 IDE 了。然后发现需要 License,这个很简单,去官网申请一下,一天邮件就下来了。

接下来配置 License,IDE 很容易,直接选择邮件里发下来的 node-locked License 即可。不过 Synplify Pro 的 Linux 版本不支持直接单文件 node-locked 的 License,只允许跑 SCL … 不过高云也提供了 SCL 的下载,和 IDE 的 License Server 放在一起,安装完以后,在得到的 License 里加上两行:

SERVER ${hostname} ${hostid} ${port}
VENDER snpslmd /path/to/scl/2018.06/linux64/bin/snpslmd

然后把 $LM_LICENSE_FILE 指向这个文件路径,就可以了。这一部分感谢 @Jackey-Huo。

随手写了一个简化版的点亮数字人生(没有数码管),得到了 bistream,准备往板子里刷,然后问题出现了:

ImportError: /path/to/Gowin_YunYuan_V1.9.0Beta_linux/Programmer/bin/librt.so.1: symbol __vdso_clock_gettime version GLIBC_PRIVATE not defined in file libc.so.6 with link time reference

目测是 glibc 版本问题 … 这就很难处理了。另外又从官网下载了独立的 Programmer,仍然不行,检测不到设备。

最后想了想,找到了终极办法,在 Docker 里运行 CentOS 的 Privileged Container,再跑 programmer:

$ docker pull centos
$ docker run -it --privileged -v /home:/home centos /bin/bash

CentOS 镜像出乎意料地小。进去以后,找到 Programmer 路径,然后 scan:

# ./programmer_cli --scan
 Scanning!
Current download-cable channel:0
Device Info:
        Family: GW2A
        Name: GW2A-18
        ID: 0xREDACTED
 1 device(s) found!
 Cost 0.54 second(s)

接着烧到 SRAM 中:

# ./programmer_cli -d GW2A-18 --fsFile /path/to/bitstream.fs --run 2
 "SRAM Program" starting on device-1...
Programming...: [#########################] 100%
 User Code: 0xREDACTED
 Status Code: 0xREDACTED
 Cost 4.54 second(s)

烧录成功,功能测试也没有问题。可以继续进行下一步工作了。

每周分享第 17 期

对不起咕咕了两天。。

  1. 关于 rel="noopener" 的一些细节 https://mathiasbynens.github.io/rel-noopener
  2. Microsft Defender 推出 macOS 版 https://arstechnica.com/gadgets/2019/03/microsoft-ships-anti-virus-for-macos-as-windows-defender-becomes-microsoft-defender/
  3. 基于 Docker 的快速启动在线 Linux box https://github.com/instantbox/instantbox
  4. rust 在窗口中显示 fb 的库 https://github.com/emoon/rust_minifb
  5. Vue 列表空间,为大量数据设计 https://github.com/tangbc/vue-virtual-scroll-list
  6. musl 各平台的交叉编译工具链 https://musl.cc/
  7. 在线的 markdown 转 pdf https://md2pdf.netlify.com/
  8. 一本关于 C64 的书 http://10print.org/
  9. 转自 dram "haskutil tql,自动更新 import,自动 {-# LANGUAGE #-} ,自动填 hole" https://github.com/EduardSergeev/vscode-haskutil
  10. ZFS on Linux 加入 TRIM 支持 http://www.phoronix.com/scan.php?page=news_item&px=ZFS-On-Linux-TRIM-Lands
  11. 又一个体现 JS 玄学之处的网站 https://getify.github.io/coercions-grid/

静态编译 sqlite3

最近 rCore 支持了动态链接库,于是想着在测试 sqlite 的时候直接用动态的,不过出现了玄学的问题,它会访问一个不存在的地址,看代码也没看出个所以然来。所以研究了一下 sqlite 的静态编译。首先在 configure 的时候尝试了一下:

$ ./configure CC=x86_64-linux-musl-gcc --disable-shared --enabled-static

发现 libsqlite 确实是静态了,但是 sqlite3 并不是。一番研究以后,发现是 libtool 的原因,只要这样编译:

$ make LTLINK_EXTRAS=-all-static

就可以编译出静态的 sqlite3

sqlite3: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, with debug_info, not stripped

每周分享第 16 期

继续沉迷写 OS +1

  1. C++ 的一个比较 fancy 的 format 库 https://github.com/fmtlib/fmt
  2. Rust 的 rsync 替代品 https://github.com/wchang22/lumins
  3. VirtIO 1.1 要发布了 https://github.com/oasis-tcs/virtio-docs/commit/3b4130f3a8910dad89b8166e06e58806b9c60943
  4. 向二维码嵌入图片的工具 http://cgv.cs.nthu.edu.tw/Projects/Recreational_Graphics/Halftone_QRCodes/
  5. Google Chrome 发出奇怪的 DNS 请求的原因 https://unix.stackexchange.com/questions/363512/chrome-dns-requests-with-random-dns-names-malware
  6. 转换 .HEIC 到 .jpg 的批量方案:alias heic="magick mogrify -monitor -format jpg *.HEIC" ref: Apple SE
  7. 以打代码来练打字的网站 https://typing.io/
  8. VSCode 网页 server https://github.com/codercom/code-server
  9. 奇特的 Rust 网页栈实现 https://japaric.github.io/jnet/jnet/index.html
  10. H265 解析 gui https://github.com/virinext/hevcesbrowser 之前介绍过 H264 的
  11. Awesome Rust Embedded https://github.com/rust-embedded/awesome-embedded-rust
  12. 新的 SiFive 产品 https://www.crowdsupply.com/sifive/hifive1-rev-b
  13. Wireshark 3.0.0 发布 https://www.wireshark.org/docs/relnotes/wireshark-3.0.0.html

交叉编译 Nginx 1.14.2 到 RISC-V

最近又把一定的精力放到了 RISC-V 64 上的 rCore 用户态程序的支持上,同时也借到了 HiFive Unleashed 板子,所以有真实硬件可以拿来跑了。在这之前先在 QEMU 上把能跑的都跑起来。

由于 rCore 对 glibc 的支持一直有问题,RISC-V 也不例外,所以还是选择用 musl 来做这件事情。一般搜索,终于找到了 Linux 下能用的 musl-riscv-toolchain 。编译好工具链以后,很多需要 libc 的用户态都能跑了,于是想着试一下 nginx 的编译。试着编译了一下,遇到了各种问题,最后搜到了交叉编译 Hi3536 上面使用的 nginx,里面的方法解决了这个问题。最后总结出了这样的 patch :

diff --git a/nginx-1.14.2/auto/cc/name b/nginx-1.14.2/auto/cc/name
index ded93f5..d6ab27a 100644
--- a/nginx-1.14.2/auto/cc/name
+++ b/nginx-1.14.2/auto/cc/name
@@ -7,7 +7,7 @@ if [ "$NGX_PLATFORM" != win32 ]; then

     ngx_feature="C compiler"
     ngx_feature_name=
-    ngx_feature_run=yes
+    ngx_feature_run=no
     ngx_feature_incs=
     ngx_feature_path=
     ngx_feature_libs=
diff --git a/nginx-1.14.2/auto/lib/openssl/make b/nginx-1.14.2/auto/lib/openssl/make
index 126a238..7a0e768 100644
--- a/nginx-1.14.2/auto/lib/openssl/make
+++ b/nginx-1.14.2/auto/lib/openssl/make
@@ -51,7 +51,7 @@ END
 $OPENSSL/.openssl/include/openssl/ssl.h:   $NGX_MAKEFILE
    cd $OPENSSL \\
    && if [ -f Makefile ]; then \$(MAKE) clean; fi \\
-   && ./config --prefix=$ngx_prefix no-shared no-threads $OPENSSL_OPT \\
+   && ./config --prefix=$ngx_prefix no-shared no-threads --cross-compile-prefix=riscv64-linux-musl- $OPENSSL_OPT \\
    && \$(MAKE) \\
    && \$(MAKE) install_sw LIBDIR=lib

diff --git a/nginx-1.14.2/auto/types/sizeof b/nginx-1.14.2/auto/types/sizeof
index 480d8cf..52c7287 100644
--- a/nginx-1.14.2/auto/types/sizeof
+++ b/nginx-1.14.2/auto/types/sizeof
@@ -33,7 +33,7 @@ int main(void) {
 END


-ngx_test="$CC $CC_TEST_FLAGS $CC_AUX_FLAGS \
+ngx_test="gcc $CC_TEST_FLAGS $CC_AUX_FLAGS \
           -o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_LD_OPT $ngx_feature_libs"

 eval "$ngx_test >> $NGX_AUTOCONF_ERR 2>&1"

接着,在 ./configure --with-cc=riscv64-linux-musl-gcc --with-cc-opt=-static --with-ld-opt=-static --without-pcre --without-http_rewrite_module --without-http_gzip_module --with-poll_module --without-http_upstream_zone_module 之后,修改 objs/ngx_auto_config.h,加入:

#ifndef NGX_SYS_NERR
#define NGX_SYS_NERR  132
#endif

#ifndef NGX_HAVE_SYSVSHM
#define NGX_HAVE_SYSVSHM 1
#endif

接着就可以正常编译了:

$ file objs/nginx
objs/nginx: ELF 64-bit LSB executable, UCB RISC-V, version 1 (SYSV), statically linked, with debug_info, not stripped

在古老的 OS 上运行一个干净的新的环境

由于某些课程的原因,需要在一个 CentOS 7 上跑一些编译和运行代码。看到这么古老的软件,我心想不行,肯定要找新一些的软件来用。首先想到的是 tmux,于是按照网上的脚本 很快装了一个 tmux 2.8 版本,果然好了很多。但是常用的很多软件依然是个问题。试了一下最近比较新的 code-server,因为 ABI 问题跑不起来。

于是开始想玩骚操作。首先想到的是 Gentoo Prefix,不过既然是别人的机器,还是算了。又找了 fakeroot 配合 alpine rootfs 的方案,但编译不过,估计是内核版本问题。又试了一下 fakechroot,但它需要配合 fakeroot 使用,这就凉了。

然后又找了一些替代方案。一是 uchroot,但由于 CMake 版本太老也编译不过。最后发现了 PRoot ,直接提供 prebuilt 然后很容易就可以跑起来:

$ ./proot -b /proc -b /dev -r $CHROOT /bin/busybox sh

于是就进到了 alpine 的 rootfs 中,下载地址。进去以后发现没有编辑器,于是出来把 apk 的源改了,加了 resolv.conf,就成功地安装了很多很新的软件了。

每周分享第 15 期

继续沉迷写 OS

  1. 嵌入 graphics for rust 可以绘制 bmp 了 https://wapl.es/rust/2019/03/04/embedded-graphics-0.4.7-bmp-support.html
  2. rustup component history 方便 nightly 日期选择 https://rust-lang.github.io/rustup-components-history/index.html
  3. grpcurl 用于 grpc 调试 https://github.com/fullstorydev/grpcurl
  4. grafana 6.0 is out http://docs.grafana.org/guides/whats-new-in-v6-0/
  5. 康哥推荐的写论文工具:https://github.com/stsewd/ieee-pandoc-template

每周分享第 14 期

最近沉迷写 OS,没怎么搜罗新的东西(

  1. 用 Docker 做交叉编译 https://github.com/dockcross/dockcross#dockcross
  2. tar inplace extraction 有意思的思路 https://gitlab.com/antonok/taro
  3. los 16.0 发布 基于 Android Pie https://lineageos.org/Changelog-22/
  4. Rust 1.33.0 Pin 进入 stable https://blog.rust-lang.org/2019/02/28/Rust-1.33.0.html
  5. Rust 用于编写 cli 软件的工具库 https://rust-lang-nursery.github.io/cli-wg/index.html
  6. 自动使用 CI 发布二进制 prebuilt 的模板 https://github.com/japaric/trust
  7. 转换 gif 到 xlsx 很神奇 https://github.com/pugwonk/gif2xlsx/blob/master/README.md
  8. Go REPL https://github.com/cosmos72/gomacro
  9. 基于区块链的论坛海星 其实是区块链数据库 https://github.com/CovenantSQL/CovenantForum
  10. Rime emoji 嵌入输入法 https://github.com/rime/rime-emoji
  11. 一个神奇的数据库 https://github.com/mit-pdos/noria
  12. 一个讲内核的 gitbook https://richardweiyang.gitbooks.io/kernel-exploring/
  13. 用 React 写桌面控件 http://tracesof.net/uebersicht/

在 rCore 上运行 nginx

阿 西 吧 nginx 终于能在 rCore 上跑了 orrrrrrrz

通过这半个多月来的大量开发,我和王润基 @wangrunji0408 学长算是终于完成了第一个 milestone:跑起来一个 nginx。遇到了很多困难,大概有这些:

  1. syscall 实现不全。各种方面都缺,然后 nginx 在编译的时候又检测到比较新的 OS 版本,所以很多 syscall 都用了新的来替代老的,例如 readv/writev pread/pwrite accept4 等等,所以这方面做了一些工作。另外,还有很多新的 syscall 进来,太多了我就不细说了,基本上一个 commit 做一点一个 commit 做一点这个样子。
  2. nginx 用到了 SSE 的寄存器 xmm,但是之前是没有开的。所以把 sse 打开,然后切换上下文的时候把 sse 通过 fxsave 保存和 fxrstor 恢复(有意思的是,as 居然不认这俩,只好手动写字节码),然后为了 16bit 的对齐又写了几行汇编代码。这块问题不大,今天一会就搞定了。但是如果要性能更高一些的话,可能需要在第一次使用 xmm 的时候再开始保存,大概就是加一个 bit 的事情。
  3. 文件系统有点崩。实现还是有很多 BUG,表现就是需要经常重新 mksfs 一下,再重启加载完好的 fs,有时候强制关机一下就又崩了。
  4. 内存管理做了一些改变。为了实现更加完整的 mmap mumap 和 mprotect,又发现了一些新的 BUG 在里面,然后慢慢修复了。就是实现的有点粗暴。
  5. 死锁问题。这个其实现在还会出现,只是还没调出来,也不会百分百出现。我们计划在锁上面做一些死锁检测,例如记住是谁上锁的,等等。现在就遇到一个很玄学的死锁问题。

然后代码也是一边在写一边在重构吧,很多地方现在都写得很粗暴,FIXME 和 TODO 留了很多,很多地方也写得不够优雅。以后再慢慢重构 + 优化吧。

截图留念:

再往前的话,还有很多小的问题,例如网卡的中断启用了但没有改 mask,所以啥也没收到,靠 QEMU Tracing 找到问题。还有一个很有意思的现象,就是如果 elf 的 program header 没有 phdr 这个项的时候,我们发现,可以通过第一个 load(如果加载了完整的 elf 头的话),我们可以从这里推断出 phdr 的地址(load 的虚拟地址加偏移),然后丢到 auxv 里去让 musl 配置 tls。总之这些都解决了。也不用去考虑兼容 litc 了,已经全部向 linux 靠拢了,稳。

注:最简 nginx 编译参数:

./configure --with-cc=/usr/bin/musl-gcc --with-cc-opt=-static --with-ld-opt=-satic --without-pcre --without-http_rewrite_module --without-http_gzip_module --with-poll_module

这样编译出来是一个静态文件,并且在 strip 之后只有不到 1M 的大小。

最简 nginx 配置:

daemon off;
master_process off;

events {
    use poll;
}

http {
    server {
        listen 80;
        server_name _;

        root /;
    }
}

这样就免去了一些麻烦(多线程、多进程交互还是有很多问题),但确实可以跑起来了。

另外,还需要写一份 /etc/passwd 和 /etc/group 用于 nobody 和 nogroup。不需要其他额外的东西了。

实现网络的 syscall

有了网卡驱动,接下来要做的就做网络的 syscall 了。为了测试,首先在 busybox 里找可以用来测试的 applet,由于没有实现 poll,所以 nc telnet 啥的都用不了。最后选择到了 ping 和 pscan 上。

ping 大家都很了解,pscan 就是一个扫端口的,对一个 ip 连续的若干个端口发起 tcp 请求。这就要求我提供 raw socket 和 tcp socket 状态的支持。由于网络栈本身是异步的,但 read connect 这些函数在不调 setsockopt 的前提下又是同步的,然而现在又没有 signal 可以用,要是 block 了就再也出不来了。于是就采用了 Condvar 的办法,拿一个全局的条件变量,当 poll 不到内容的时候,先把线程拿掉,等到网络栈更新了,再恢复。这样至少不会把 cpu 也 block 住。

然后就是把 socket 部分改了又改吧,数据结构的设计改了几次,为了解决 ownership 问题上锁啊也有点多,但是也更细了,虽然实际上可能没有必要,因为上面还有大的锁。不过性能还不是现在考虑的重点,关键还要先把 send recv accept bind listen 啥的写得差不多了,然后还有把 poll/select 实现了,这个很关键。

中间遇到的最大的坑就是,接收 pci interrupt 的时候总是啥也没有,然后靠万能的 qemu trace 发现,原来是 mask 掉了,所以啥也收不了,然后最后的解决方案就是用 MSI Interrupt #55 搞定了这个问题。至于为啥是 55 呢,因为 23 + 32 = 55 啊(误

总之是修好了。终于可以继续写其它的 syscall 了。还没想好 poll 要怎么写,orz。

每周分享第 13 期

今天还是早点发吧

  1. Rust concurrent work-stealing queue 可能可以放到调度器里用 https://github.com/kinghajj/deque
  2. 支持信用卡/时间等格式信息的输入控件 https://github.com/nosir/cleave.js
  3. Rust 的简单 http server https://github.com/svenstaro/miniserve
  4. Coroutines & Modules Added For C++20 http://www.phoronix.com/scan.php?page=news_item&px=Coroutines-Modules-CPP20
  5. Implementing TCP in Rust (part 1) https://www.youtube.com/watch?v=bzja9fQWzdA
  6. 一个类似 Vuetify 的框架 https://buefy.org/
  7. systemd tmpfiles.d 用于管理特定的临时文件夹 https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html
  8. 获取 ASN 信息的 API https://bgpview.docs.apiary.io/#reference/0/asn-prefixes/view-asn-prefixes
  9. Rust getter/setter generation https://github.com/Hoverbear/getset
  10. Golang 1.12 发布:TLS 1.3 http://www.phoronix.com/scan.php?page=news_item&px=Golang-1.12-Released
  11. 一个笑话 https://redd.it/aux77g
  12. Rust 的 STM https://github.com/mtak-/swym
  13. 自动同步视频和字幕 https://github.com/smacke/subsync
  14. 用于快速开发的 http server https://zeroserver.io/
  15. 一个自动加载的 http server https://github.com/tapio/live-server
  16. 用 markdown 写 presentation 在线版 https://github.com/gnab/remark
  17. 解决 excel 读取 nodejs 生成的 csv 的乱码问题 https://github.com/f2e-journey/xueqianban/issues/34
  18. 清华的 Rust OS https://github.com/oscourse-tsinghua/rcore_plus
  19. GPU 加速的 JS http://gpu.rocks/
  20. 小型的 k8s 实现:k3s https://k3s.io/
  21. 粗暴的隧道,极低的带宽,有啥用 https://0day.work/performance-of-iodine-over-dns-over-https/
  22. Caddy 乘着 Go 官方支持的车轻松加入 TLS1.3 家族 https://github.com/mholt/caddy/pull/2399/files

使用 Rust 实现 e1000 驱动

是的。我又来了。上次做了使用 Rust 实现 VirtIO 驱动之后,继续往 rCore 加更多的驱动支持。由于现在工作重点是 x86_64 下的 syscall 实现,所以选了一个比较有代表性的驱动 e1000 来实现。其实如果只是为了在 qemu 下运行的话,其实只需要支持 virtio-pci 就可以了,原来的 virtio-net 直接拿来用就可以了。

为什么挑 e1000 呢,一方面是支持的设备多,有真实硬件可以测试,虽然不一定要裸机上跑,但是可以通过 PCI passthrough 来测试驱动的正确性。另一方面是网上的资料比较多,有现成的简单的代码可以借鉴。这次主要借鉴了三个来源:一是 Biscuit OS,二是 Judge Duck OS,三是 Linux。

首先是实现了简单的 PCI 总线的枚举,然后找到对应的设备,激活,并且找到映射的内存地址,然后把原来 C 语言的实现搬运到 Rust 中。这个过程中遇到很多坑,例如一开始我以为内核里 pa 和 va 是一个固定的偏移,不过多次尝试后才发现这个假设只对 riscv 平台里的实现成立。

这个时候就可以收到外面给进来的以太网帧了。接着就是把它接入到 smoltcp 的 API 中。但是发包又不工作了,尝试了很多次,各种方法也不行。其中特别要提到的就是 qemu 的 tracing API,它在帮助我调试之前的 virtio 驱动和这次的 e1000(e) 驱动中起到了很大的帮助。不过,遗憾的是,发包相关的代码里的 trace 不足以让我找到问题的所在,我只好采用了最后一招:

下载 QEMU,自己改,然后自己编译。

这个方法果然很有效啊,经过简单的几个修改,很快就定位到问题所在了,原来就是一个简单的错误,把 4 写成了 8。这个过程中我也发现 QEMU 在 incremental build 的时候似乎会 segfault,我没管这么多,反正编译也不慢,次数也不多,每次 clean 再 build 问题也不大。

接下来要摸索 82559 的网卡适用情况如何,因为有一个真实的 82559 网卡可供测试。另一方面就要开始考虑 socket 那一套 syscall 怎么做了。

每周分享第 12 期

看脑王迟到了(

  1. Rust Embedded Graphics 库 https://github.com/jamwaffles/embedded-graphics
  2. Wireguard for macOS https://lists.zx2c4.com/pipermail/wireguard/2019-February/003853.html
  3. Wireguard-go UI https://github.com/aequitas/macos-menubar-wireguard
  4. T2 逆向 https://duo.com/labs/research/apple-t2-xpc
  5. 脑王剪辑 https://www.bilibili.com/video/av43972717
  6. dram 推荐的工具 notion.so
  7. 带授权和审计的 sudo https://github.com/square/sudo_pair
  8. rust 的 manpage 生成器 https://github.com/rust-cli/man
  9. 才发现 Github 有 Project 功能 https://help.github.com/en/articles/about-project-boards
  10. rust 的 elf/macho 生成 https://github.com/m4b/faerie
  11. rust 的 elf/macho/pe 读取 https://github.com/m4b/goblin
  12. rust 的 glob 实现 https://github.com/rust-lang-nursery/glob
  13. 基于 BoringSSL 的 rust 加密库 https://mundane.googlesource.com/mundane
  14. 从 linux 交叉编译到 macOS 的方法 https://github.com/tpoechtrager/osxcross
  15. cargo-deb 后又有了 cargo-rpm https://github.com/RustRPM/cargo-rpm
  16. 自带 diff 的 assert_eq 宏 https://github.com/colin-kiegel/rust-pretty-assertions
  17. Learn Helper 4.0 哈利版发布 https://chrome.google.com/webstore/detail/learn-helper/mdehapphdlihjjgkhmoiknmnhcjpjall
  18. 检查依赖中存在的 unsafe https://github.com/anderejd/cargo-geiger

预告 Learn-Project 4.0 版

不知道大家听没听说过 Google Chrome 有个插件,叫做 Learn Project,是一个网络学堂的改良前端。不过,随着网络学堂的不断更(xia)新(gao),它已经不再适用于现在的版本。于是,哈利橙(@Harry-Chen)决定利用寒假时间,在今天(2.17)之前完成 Learn Project 4.0 的开发。为什么叫 4.0 呢,因为哈利橙课程的 GPA 众数是 4.0。这个版本采用先进的 React 框架编写(Vue 塞高),使用了 Material Ui 作为界面框架(这我觉得可以),目前的一个参考图(图源哈利橙):

看那满眼的高分数,希望 Learn Project 4.0 能在 flag 倒下之前完成。

每周分享第 11 期

对不起迟到了(

  1. Google 的 fuzzing 家族又来了个新成员 太喜欢造轮子了 https://github.com/google/clusterfuzz
  2. 有趣的文本生成工具 https://github.com/TheBerkin/rant
  3. 又一个 side channel attack https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2019/february/downgrade-attack-on-tls-1.3-and-vulnerabilities-in-major-tls-libraries/
  4. 来自 Prof Fan 的一个 cf dns 权限限制分发的工具 https://github.com/ProfFan/cloudflare-proxy-rs
  5. 部署到了我的博客的小工具 大家体会到了吗 https://instant.page/
  6. 教育性质的 C on web https://vasyop.github.io/miniC-hosting/
  7. 激活清华教育版 win10? --> dig +short TXT win10.harrychen.xyz
  8. windows95 v2.0 发布(误 https://github.com/felixrieseberg/windows95/releases/tag/v2.0.0
  9. 已经部署到 tuna 的 rustup-mirror crate https://github.com/jiegec/rustup-mirror
  10. Forth 也能有 http 服务端框架 http://www.1-9-9-1.com/
  11. bartender 的开源替代 实测可用 https://github.com/Mortennn/Dozer
  12. 给不支持 pac 的客户端在 http proxy 上套一层 https://github.com/williambailey/pacproxy

近来做 Stanford CS140e 的一些进展和思考(9)

距离上一篇 CS140e 系列文章已经过去了很久,距离第一篇文章过了一年零几天。在后来这一段时间内,CS140e 结束了课程,又开始了新一年的 winter 2019 课程,迎来的却是 C 版本的 CS140e,不禁让人感到失望。还好,Sergio Benitez 放出了原来的 CS140e 的镜像,如果大家仍然想回去查看原版优质的 CS140e,可以点进去参考。

后来因为机缘巧合参与到了清华的 Rust OS 课程,又想到回来把原来的 CS140e 进行更新,于是顺带把跑在 QEMU 下的一些需要的工作给做了,另外把 Rust nightly 版本更新了(一年前的 nightly 还能叫 nightly?),才发现标准库变化还是蛮大的,由于 nightly 版本变了,而且原来是内嵌了一个阉割过的 std,所以主要是从新的 std 里抄代码到内嵌的 std 中。另外,原来的 xargo 也不再维护了,转而使用 rust-xbuild 进行交叉编译。

然后又顺手实现了 backtrace 和从 backtrace 中配合 dward symbols 找函数名的功能,不过实践证明,这些东西还是 addr2line 做得更好,所以也就没有做下去,在 relocation 上也是遇到了各种问题。这个经验也是应用到了 rCore 那边。

再之后也就是寒假写驱动了,见之前的一个博文,我就没有在 CS140e 上去实现它了。有时间有兴趣的时候再考虑做一下 Raspberry Pi 的网卡驱动吧。

写于迪拜雨天。

每周分享第 10 期

新年快乐!

  1. Haskell to WASM compiler https://github.com/tweag/asterius
  2. A ssh tarpit that sends endless banner https://github.com/skeeto/endlessh
  3. Use rust to write UEFI app https://github.com/rust-osdev/uefi-rs
  4. 神奇的小游戏 https://yeahpython.github.io/game/game.html
  5. 又一个后端 rust web 框架 https://github.com/seanmonstar/warp
  6. 挺酷的一个网站 https://github.com/pomber/github-history
  7. ws 调试工具 https://github.com/vi/websocat
  8. React Hooks 进入正式版 https://reactjs.org/blog/2019/02/06/react-v16.8.0.html
  9. 又一个关于硬件的 wiki https://wikidevi.com/wiki/Main_Page
  10. virtio-fs 项目 旨在替代 virtio-9p https://virtio-fs.gitlab.io/
  11. SergioBenitez 镜像了一份去年的 CS140e https://cs140e.sergio.bz/

每周分享第 9 期

  1. IDA Loader plugin for some Nintendo rom https://github.com/w4kfu/IDA_loader
  2. websocket daemon http://websocketd.com/
  3. 实时的游戏开发器 https://script-8.github.io/
  4. 硬核逆向 Leica 相机的固件 https://alexhude.github.io/2019/01/24/hacking-leica-m240.html
  5. 做 CTF 时遇到的整数溢出的 CVE https://www.anquanke.com/post/id/104182
  6. 发现一个 speedtest 的轮子 https://github.com/adolfintel/speedtest
  7. 西数的 RISCV 核实现 https://github.com/westerndigitalcorporation/swerv_eh1
  8. iOS 12.2 会有更多的 PWA 兹瓷 https://twitter.com/mhartington/status/1089292031548145666
  9. 代替死去的 git-up: git config --global alias.up 'pull --rebase --autostash'
  10. 利用已知明文破解旧版加密 zip 的工具 https://github.com/kimci86/bkcrack
  11. 在线看 jwt 内容 https://jwt.io/
  12. JS 的 Lua VM https://github.com/fengari-lua/fengari
  13. 鲁棒但不优雅的前端 KV https://github.com/gruns/ImmortalDB
  14. Emacs Modules doc https://phst.eu/emacs-modules
  15. 用 IPv6 的 Flow Label 实现类似 MPLS 的效果 https://github.com/wenxin-wang/flowlabel-switching
  16. 又一个 JS 实现的表格 https://github.com/myliang/x-spreadsheet 类似以前用过的 handsontable
  17. @shankerwangmiao 推荐的光纤教程 http://www.kepu.net.cn/gb/technology/telecom/fiber/fbr215.html
  18. 用 Rust 写 iOS App https://medium.com/visly/rust-on-ios-39f799b3c1dd
  19. Build Once, Run Anywhere 还行 https://wasmer.io/
  20. 挺科学的 DNS proxy https://github.com/AdguardTeam/dnsproxy

使用 Rust 实现 VirtIO 驱动

背景

最近在给 rCore 添加驱动层的支持。一开始是想做网卡驱动,后来发现, qemu-system-riscv32 只支持如下的驱动:

# qemu-system-riscv32 -device help

Storage devices:
name "scsi-cd", bus SCSI, desc "virtual SCSI CD-ROM"
name "scsi-disk", bus SCSI, desc "virtual SCSI disk or CD-ROM (legacy)"
name "scsi-hd", bus SCSI, desc "virtual SCSI disk"
name "virtio-blk-device", bus virtio-bus
name "virtio-scsi-device", bus virtio-bus

Network devices:
name "virtio-net-device", bus virtio-bus

Input devices:
name "virtconsole", bus virtio-serial-bus
name "virtio-keyboard-device", bus virtio-bus
name "virtio-mouse-device", bus virtio-bus
name "virtio-serial-device", bus virtio-bus
name "virtio-tablet-device", bus virtio-bus
name "virtserialport", bus virtio-serial-bus

Display devices:
name "virtio-gpu-device", bus virtio-bus

Misc devices:
name "loader", desc "Generic Loader"
name "virtio-balloon-device", bus virtio-bus
name "virtio-crypto-device", bus virtio-bus
name "virtio-rng-device", bus virtio-bus

所以要实现网卡的话,只能实现这里的 virtio-net-device ,而 VirtIO 驱动之间有很多共通的地方,于是顺带把 gpu mouseblk 实现了。

第一个驱动 virtio-net 的实现

首先想到并且实现了的是网卡驱动, virtio-net 。最开始的时候,为了简单,只开了一块缓冲区,每次同时只收/发一个包。首先拿了 device_tree-rs 读取 bbl 传过来的 dtb 地址,找到各个 virtio_mmio 总线以后按照设备类型找到对应的设备。然后就是对着 virtio 的标准死磕,同时看 Linux 和 QEMU 的源代码辅助理解,最后终于是成功地把收/发的两个 virtqueue 配置好,并且在中断的时候处理收到的包。这个时候,可以成功地输出收到的包的内容,并且发出指定内容的包了。效果就是看到了这样的图片(图中网站是 Hex Packet Decoder):

基于此,写了一个简单的以太网帧的解析,ARP 的回复和 ping 的回复(直接修改 ECHO_REQUESTECHO_REPLY 然后更新 CHECKSUM),实现了最基本的 ping:

显卡驱动

网卡可以用了,很自然地会想到做一些其他的 virtio 驱动,第一个下手的是显卡。显卡和网卡的主要区别是,网卡是两个 queue 异步作,而在显卡驱动上则是在一个 queue 上每次放一输入一输出的缓冲区来进行交互,具体步骤在 virtio 标准中也写得很清楚。在这个过程中,QEMU 的 Tracing 功能帮了很大的忙,在调试 desc 的结构上提供了很多帮助。

然后就在 framebuffer 上画了一个 mandelbrot:

在 @shankerwangmiao 的建议下,调了一下颜色:

这样就好看多了。

HTTP 服务器

在 @wangrunji0408 的提醒和建议下,我开始把一个 Rust 实现的网络栈 smoltcp 集成到代码中来。这个库中,对底层 Interface 的要求如下:

  1. 当可以发包并且可以收包的时候,返回一收一发两个 Token,并在使用的时候调用指定的函数。
  2. 当可以发包的时候,返回一个发的 Token,含义同上。

这是我第一次看到这种抽象,而且也没有特别明确的文档表示,这个 Token 代表什么,我应该提供什么。我直接按照一些已有的例子,照着实现了一把。过程中遇到了 ownership 的问题,通过 Arc 和 Mutex 解决了,然后又出现了死锁的问题,调了半天才调出来。

接着按照 somltcp 的样例写一个简单的 udp echo server 和(假的)tcp 服务器:

// simple http server
let mut socket = sockets.get::<TcpSocket>(tcp_handle);
if !socket.is_open() {
    socket.listen(80).unwrap();
}

if socket.can_send() {
    write!(socket, "HTTP/1.1 200 OK\r\nServer: rCore\r\nContent-Length: 13\r\nContent-Type: text/html\r\nConnection: Closed\r\n\r\nHello, world!\r\n").unwrap();
    socket.close();
}

虽然很粗暴,但是 work 了:

鼠标驱动和块设备驱动

接着自然是往 QEMU 支持的剩下的 virtio 设备里下手。首先下手的是鼠标驱动。这次遇到了新的问题:

  1. 由于缓冲的存在,每次只有在 EV_SYN 的时候才会一次性把若干个事件放入队列中。
  2. 一个事件就要一个 desc chain,意味着直接串足够大小的 buffer 到同一个 desc chain 中并不能工作。

于是只好痛定思痛照着 Linux 内核的实现把完整的 Virtqueue 的操作实现了,并且顺带把前面的网卡和显卡的驱动也更新了。果然,每次都是三个左右的事件(X,Y,SYN)插入,然后根据这些事件就可以计算出当前的鼠标位置了。

至于块设备,遇到的则是别的坑。看标准的时候,本以为就一个结构体 virtio_blk_req 就搞完了,但仔细读了读,标准似乎没讲清楚,读的时候是怎么传,写的时候又是怎么传。于是在这里卡了很久,从 Tracing 信息可以看出,QEMU 一直认为我提供的 buffer 大小不正确,多次实验之后发现,给 device 写入的 buffer 大小为 block size 的整数倍加一,这个一存放的是状态,其他则是数据,真的太坑了。

有了块设备以后,就可以替换掉原来的内嵌 SFS 的方案,转为直接从块设备读 SFS 文件。这里我没想明白 lazy_static 和 ownership 的一些问题,最后也则是@wangrunji0408 的帮助我解决了。

总结

用 Rust 写出一个可以工作的驱动并不难,只要知道 unsafe 怎么用,但是一旦需要深入思考这里应该用什么安全的方法封装的时候,才发现是个很困难的事情。现在虽然工作了,但是很多地方线程并不安全,代码也不够简洁高效,以后还有很多需要改进的地方。

See also

  1. Virtio Spec

THUWC 2019 小记

前段时间,以工作人员的身份参加了在广州二中举办的 THUWC 2019。作为一只菜鸡 OI 选手,我没想到过我会以另一种身份参与一个我本来没能参与的活动,就好像以暑校辅导员的身份参与清华暑校一样。

提早来到了赛场,布置场地,然后把机考的各个流程都过一遍,记住各个细节,各方面都有条不紊地进行,看着第一场前同学们特别激动地冲入考场,到最后一场同学们考完后的释放,在同学们身上看到了很多不成熟的样子,看到了兴奋想要和同伴分享的喜悦,也看到了不甘的眼泪。

希望各位强大的选手们可以来到九字班、零字班乃至一字班,享受课改的乐趣吧嘿嘿嘿