# Plan 9.r post 02/B4BH - AHCI patch, creating isos and stupid ideas Tags: [[pub/@9rhiz]] [[pub/@English]] $[[pub/9rhiz-disc]] ## AHCI ```patch From 3e81bb2f1b9ab78c0f140a7526414252406f552f Mon Sep 17 00:00:00 2001 From: Rhizoome Date: Sat, 11 Apr 2026 04:03:18 +0200 Subject: [PATCH] 144d/a801: vendor runs this ssd in ahci mode S4LN058A01[SSUBX] AHCI SSD Controller (vendor slot) -- @pub @kernel @hardware --- sys/src/9/pc/sdiahci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sys/src/9/pc/sdiahci.c b/sys/src/9/pc/sdiahci.c index 3207fb57..aada71df 100644 --- a/sys/src/9/pc/sdiahci.c +++ b/sys/src/9/pc/sdiahci.c @@ -2073,6 +2073,9 @@ didtype(Pcidev *p) switch(p->vid){ default: return -1; + case 0x144d: + if((p->did & 0xffff) == 0xa801) + return Tahci; /* Samsung SSD AHCI vendor special */ case 0x8086: if((p->did & 0xffff) == 0x1e02) return Tich; /* c210 */ -- 2.53.0 ``` I have a first patch. 9front uses a fixed list of PCI devices it considers valid AHCI controllers plus this line `if(p->ccrb == Pcibcstore && p->ccru == 6 && p->ccrp == 1)` - I guess if the device correctly identifies itself. The vendor of my device asked Samsung to create a proprietary interface for this SSD and to put an AHCI controller on the SSD. You can buy adapters for that interface and you can connect a normal NVMe SSD to it; which is what I did for my main device and its backup. I knew that the AHCI-SSD runs as normal SATA3 device on Linux, so I assumed it'll work fine if 9front detects it. ## igfx I also ran the kernel with my print statements the first time and the ones I added to vgaigfx.c did not appear, but the ones I used to debug the AHCI device did. So it remains a mystery how 9front displays the native graphics modes of the device. ## Development I decided to do all [my development](https://git.sr.ht/~rhizoome/9front) in the main 9front repo. This means scripts, tools and kernel patches all live in that repo. 9front integrated the repo fully: The 9front repo is the same structure as the "root" partition. I did the multiple branches for each sub-project dance for a long time and its too tedious; so I will just mark the project and my intents in the commit using `@tag`. I also found other people publishing patches against the 9front repo and I mirrored their repos locally. ## Learning I learned a lot about plan9. There is a good entry in 9fronts wiki that is called: [unix2plan9](https://wiki.9front.org/unix2plan9.html). So now I am holding much less tools wrong. A funny example was `walk`; it is very central to plan9 and I didn't find it. I wanted `find` and I couldn't find the replacement. So I used `du`, which does the same as `walk` but also displays disk usage. ### Non-disappointing 9pfs [Here](https://github.com/ftrvxmtrx/9pfs) someone ported 9p from some kind of 9front. Its been handed over many times. You need to compile it against fuse2, since I usually have fuse3 installed, I just compiled it to a static binary. So the problems I reported in the [[pub/B46A]]{last post} are fixed with this. ## Building 9front I added port 4241 which gives you raw access to rc. It will display stderr on the serial-console, I didn't find out how to get both: raw rc and stderr redirection. But for scripting the build process I don't need stderr, just for debugging; so it is good enough. [The script](https://git.sr.ht/~rhizoome/mond) is my terrible lua code repo has been updated. - `hostfwd=tcp:127.0.0.1:4241-:4241` - `aux/listen1 -t tcp!*!4241 rc &` I use lua-socket to connect to rc and send commands directly. While plan9s namespaces are just awesome, for my scripting it is a bit tedious. I could not convince `aux/listen1` to just use the users namespace. I think it actually not possible in plan9. So you have to store the steps to get a the correct namespace in a script and execute it every time you connect. Checkout the output of `ns` if you are following this on 9front. ### Sysupdate ```lua function nf:sysupdate() self:exec("git/get git://10.0.2.2/srv/9front.git", true, true) self:exec("git/pull -f -u origin", true, true) self:exec("rm adm/timezone/Egypt", true) self:exec("rm adm/timezone/README", true) self:exec("git/branch 9rhiz", true, true) self:exec("git/pull -u origin", true, true) self:exec("git/revert .", true) self:exec("cd /lib/firmware && git/init -u git://10.0.2.2/srv/firmware.git", true, true) self:exec("cd /lib/firmware && git/get git://10.0.2.2/srv/firmware.git", true, true) self:exec("cd /lib/firmware && git/pull -f -u origin", true, true) self:exec("cd /lib/firmware && git/branch front", true, true) self:exec("cd /lib/firmware && git/pull -u origin", true, true) self:exec("cd /lib/firmware && git/revert .", true, true) self:exec("rm -rf /lib/firmware/.git", true, true) end ``` I am just following [FQA 5 - Building the System from Source](https://fqa.9front.org/fqa5.html). By default any `exec()` binds the 9front repo into the root: `bind -ac /dist/9front /; cd /`. plan9 is absolutely awesome with using binding consistently. Replacing many POSIX features effortless. And the 9front repo is the same content as your `root`. Git9 has a bit of trouble talking to git-daemon. I don't want to serve git using a 9front VM and since I got it working; it is good enough. All the `git/get` and `git/pull` commands are basically redundant, but each fails in a different way and together they actually do a correct pull. ### Build ```lua function nf:build() self:exec(". /sys/lib/rootstub", true) self:exec("cd /sys/src && mk install", true) self:exec("cd /sys/man && mk", true) self:exec("cd /sys/src/9/pc64 && mk install", true) end ``` Rebuilding the complete system is very simple. Just `mk install` /sys/src and /sys/src/9/pc64 (the kernel). The branch [9rhiz](https://git.sr.ht/~rhizoome/9front) points to the same git-revision as the release of 9front I am using. I didn't add any new commits from upstream; just my AHCI fix. ### ISO ```lua function nf:iso() self:exec( "bind /root /n/src9 && " .. "bind -ac /dist/9front /n/src9 && " .. "mkdir -p /n/src9/acme/bin/68020 && " .. "mkdir -p /n/src9/acme/bin/68000 && " .. "cd /sys/lib/dist && " .. "mk /tmp/9front.amd64.iso", true) end ``` Both iso creation and `inst/start` (the installer) use the content of the current system. `/` is a mixture of many binds and mounts, but the clean root is stored at `/root`. The `mk`, the installer and the iso creation process use proto-files. The global ones are stored in `/sys/lib/sysconfig/proto`. These files define how to install things. ```proto mode=ug+rw mode=o-w uid=sys gid=sys adm d775 adm adm uid=adm gid=adm timezone d775 * ``` `sys/lib/sysconfig/proto/distproto` starts like this. To me it is seems to be a good thing. For example the warnings I got during iso creation showed me that I was missing the contents of `/lib/firmware`. Probably the only downside is the maintenance; the iso creation also warns about files that aren't part of 9front anymore. So people forgot to remove them from the proto-files. ## Install I used my iso to install 9front on my test-device with the AHCI-SSD. It didn't create the `efi` partitions content correctly. Actually the `/EFI` directory was missing completely. I believe the iso downloaded from 9front.org did that correctly. But I just needed to copy the `/EFI` directory of the iso manually. I think the iso you can download is created slightly different, but I am not sure. The iso meta-data looked different. But the system now works, runs `gefs` and I can continue to fix the hardware quirks. ## Some kind of stupid idea I do a lot of exploration using a editor, some source-code and entr. entr runs arbitrary commands when it detects a change in files you gave it via stdin. So I write, save, check, write, save, check, write, save, check, write, save, check.... It actually works almost as well with compiled languages, but not having to worry about types and memory during exploration is a very important bonus. I desperately wanted a good version of lua on 9front. Lua has about 90 opcodes. This is a switch with 90 cases. `9c` will just create 90 ifs (CMPL plus JEQ) in a row. ```asm CMPL CX,$2 JEQ ,-4(PC) ``` I know that lua is very optimized. The developers actually optimize the order of the opcodes, do branch-prediction analysis and tests on less complex compiler-chains. But my brain just goes bananas if it knows that in the worst case 90 CMPL and JEQ are executed, when there are easy ways to get from `O(n)` to `O(1)`. There are multiple methods to create a better result. One is to do computed gotos: An array of jump targets. `9c` can't do this. Not even the assembler `9a` can do this. The complex compiler-chains emit something similar directly from the switch-statement. Another way: Implement each opcode as a function. Cproc emits the switch-statements as binary search which is `O(log n)`. With `9c` you can actually do a good `O(1)` VM. Because calling is extremely cheap. ``` struct A { int a; }; void func(struct A* a) { a->a += 1; } void main(void) { struct A a; a.a = 1; func(&a); } ``` Results in: ```asm > 6c -S c.c TEXT func+0(SB),0,$0 INCL ,(BP) RET , TEXT main+0(SB),0,$32 MOVL $1,a+-8(SP) LEAQ a+-8(SP),BP CALL ,func+0(SB) RET , END , ``` So you can implement it as an array of function pointers. I considered changing the switch-statements in luas VM to functions, but it is a lot of work and I only know if it really helps after I done it. Also from now on I maintain my own fork of lua. But the main reason: **Lua isn't very plan9.** So I thought about creating something similar as C API. Everything is dynamic, only one C-type, which represents numbers, strings and lists [tabl^] and are converted by the functions as needed and if possible. With GC and functions similar to lua. You can call all plan9 APIs. I think apart from everything now being a function like `add(list)`. I could actually create quite a nice syntax. Back in 2015 I made similar things. As usual I like to keep things fluid until I understand better what I am doing. Also 9front has libavl so I don't have to implement hash-tables or trees (avl is a tree). Of course now I maintain a C-script-API. [tabl^]: Probably lua style tables aka a hybrid list-dict-set, but the simple version, no meta-tables.