config

config

*BSDにはconfigが完備されている。リナ方面は、どうだったかすっかり忘れているけど。で、こやつは、並にあるconfigureとは違うからね。カーネル専用のツールだ。

機能は2つ有る。一つはカーネルの製作仕様書を元に、カーネルのコンパイル環境を構築するもの。もう一つは、現存のカーネルの機能(限定的なものだけど)を変更するものだ。今回は何時もお世話になる前者の機能を調べてみる。

try config

実験である。実験の為のconfigを ~sakae/configとして用意した。勿論gdb付きね。riscvな環境にも拘わらず、arch/i386をやろうと言う無謀な試み。

riscv# /home/sakae/config
config: cannot read CONFIG: No such file or directory
riscv# /home/sakae/config TESTME
/mnt/src/sys/arch/i386/compile/TESTME/obj -> /usr/obj/sys/arch/i386/compile/TESTME
config  -b /mnt/src/sys/arch/i386/compile/TESTME/obj  -s /mnt/src/sys /mnt/src/sys/arch/i386/conf/TESTME

まずは、ご挨拶がてら引数無しで起動。デフォでCONFIGと言う仕様書が使われるとな。で、そんなのが/sys/arch/i386/configの下にないので、怒られたとな。しょうがないので、GENERICな仕様書の名前をTESTMEでcpしたものを用意。

それで実行すると、リンクが作成された。そして、改めて詳細なオプション付きでconfigが実行された。config(8)の最初の形式だね。

config [-p] [-b builddir] [-s srcdir] [config-file]

この結果

riscv# pwd
/usr/obj/sys/arch/i386/compile/TESTME
riscv# ls
Makefile        drm.h           isapnp.h        pctr.h          uhci.h
 :

後は、ここに移動してmakeするだけーーーー。世の中に有るconfigureと少しは似ているな。 強引にmakeすると

riscv# make
cat /mnt/src/sys/arch/i386/i386/genassym.cf /mnt/src/sys/arch/i386/i386/genassym.cf |  sh /mnt/src/sys/kern/genassym.sh cc -no-integrated-as -g -Werror -Wall -Wimplicit-function-declaration  -Wno-pointer-sign  -Wframe-larger-than=2047 -Wno-address-of-packed-member -Wno-constant-conversion  -Wno-unused-but-set-variable -Wno-gnu-folding-constant  -ffreestanding -fno-pie -mretpoline -O2  -pipe -nostdinc -I/mnt/src/sys -I/usr/obj/sys/arch/i386/compile/TESTME -I/mnt/src/sys/arch  -I/mnt/src/sys/dev/pci/drm/include  -I/mnt/src/sys/dev/pci/drm/include/uapi  -I/mnt/src/sys/dev/pci/drm/i915 -DDDB -DDIAGNOSTIC -DKTRACE -DACCOUNTING -DKMEMSTATS -DPTRACE -DCRYPTO -DSYSVMSG -DSYSVSEM -DSYSVSHM -DUVM_SWAP_ENCRYPT -DFFS -DFFS2 -DFFS_SOFTUPDATES -DUFS_DIRHASH -DMFS -DUDF -DMSDOSFS -DFIFO -DFUSE -DSOCKET_SPLICE -DTCP_ECN -DTCP_SIGNATURE -DPPP_BSDCOMP -DPPP_DEFLATE -DPIPEX -DMROUTING -DMPLS -DBOOT_CONFIG -DUSER_PCICONF -DAPERTURE -DMTRR -DNTFS -DSUSPEND -DHIBERNATE -DPCIVERBOSE -DEISAVERBOSE -DUSBVERBOSE -DWSDISPLAY_COMPAT_USL -DWSDISPLAY_COMPAT_RAWKBD -DWSDISPLAY_DEFAULTSCREENS="6" -DX86EMU -DONEWIREVERBOSE -DMAXUSERS=39 -D_KERNEL -MD -MP -MF assym.P > assym.h.tmp
cc: error: argument unused during compilation: '-mretpoline' [-Werror,-Wunused-command-line-argument]
 *** Error 1 in /usr/obj/sys/arch/i386/compile/TESTME (Makefile:1281 'assym.h')

riscvな環境ではi386をコンパイル出来無いと言う、当たりまえの結果となった。

出来上がったMakefileには、見所が沢山あるんで、後でじっくり見る事にして、取り敢えずの予告だけをば。

riscv# grep '.o:' Makefile | wc
    1581    3169   60737

オブジェクトファイルとソースファイルの対応を数えてみた。ターゲットはこういうのね。

acpi_machdep.o: $S/arch/i386/i386/acpi_machdep.c
acpi_wakecode.o: $S/arch/i386/i386/acpi_wakecode.S
acpi_x86.o: $S/dev/acpi/acpi_x86.c

あちこちに点在(いや、kern,arch/i388,dev,netみたいにクラスターを作ってるな)してるソースをコンパイルして、集合させてるんだな。そしてそれを組み立てるとカーネルが出来上がる。

もう一つ、油断してたんだけど、*.hの中身を点検して、びっくらこいた。並のヘッダーファイルかと思ったら、それが大違い。じっくり見ると、発見があるな。いつもは、大急ぎで通り過ぎてますから。車でさっと行くんじゃなくて、ゆっくり散歩。あるいは、新幹線じゃなくて、各駅停車でゆっくりみたいな。。。

trace config

今度は舞台をi386なマシンに移して、そこでriscv用のconfigをやってみる。config仕様は、現役のriscvなOpenBSDを作るのに使ったやつ。

こんなgdbで初めてみた。まんまのオプションを与えている。

(gdb) b main
Breakpoint 1 at 0xd07a: file main.c, line 141.
(gdb) r -b /home/sakae/KERNEL -s /usr/src/sys SEEING
Starting program: /home/sakae/config -b /home/sakae/KERNEL -s /usr/src/sys SEEING

歩兵が偵察を進めると、こんな所に到達。

        /*
         * Ready to go.  Build all the various files.
         */
=>      if (mksymlinks() || mkmakefile() || mkheaders() || mkswap() ||
            mkioconf())
                stop();
        optiondelta();
        return (0);

一瞬、or で繋げてあるんで、おやって思ったんだけど、それぞれの関数が疑を返せば(0)、次の関数にトライする。最後のmkioconf()を実行しても疑なら、結局ifは失敗。ちょいと紛らわしいけど aa() && bb() && cc() みたいなのと一緒の事だ。

後は、これらの個別の関数を覗いてみるだけね。例えば、mkheaders()の冒頭部分

B         for (fi = allfiles; fi != NULL; fi = fi->fi_next) {
  =>              if (fi->fi_flags & FI_HIDDEN)
                          continue;
                  if (fi->fi_flags & (FI_NEEDSCOUNT | FI_NEEDSFLAG) &&
                      emitcnt(fi->fi_optf))
                          return (1);
          }

ちょいと追ってみると、リンクになってた。アンチョコを見て、辿ってみる。

(gdb) p *fi
$16 = {fi_next = 0x82152080, fi_srcfile = 0x82149da0 "/usr/src/sys/conf/files",\
 fi_srcline = 82, fi_flags = 0 '\000', fi_lastc = 0 '\000', fi_nvpath = 0x8215a\
1d0, fi_base = 0x8215a1f0 "smc93cx6", fi_optx = 0x82159360, fi_optf = 0x0, fi_m\
krule = 0x0}
(gdb) p *$.fi_next
$17 = {fi_next = 0x8217c6a0, fi_srcfile = 0x82149da0 "/usr/src/sys/conf/files",\
 fi_srcline = 86, fi_flags = 0 '\000', fi_lastc = 0 '\000', fi_nvpath = 0x82154\
7a0, fi_base = 0x82154ea0 "pcdisplay_subr", fi_optx = 0x82154e70, fi_optf = 0x0\
, fi_mkrule = 0x0}
(gdb)
$18 = {fi_next = 0x821524a0, fi_srcfile = 0x82149da0 "/usr/src/sys/conf/files",\
 fi_srcline = 87, fi_flags = 0 '\000', fi_lastc = 0 '\000', fi_nvpath = 0x82159\
860, fi_base = 0x82154660 "pcdisplay_chars", fi_optx = 0x8215ab20, fi_optf = 0x\
0, fi_mkrule = 0x0}

from man

ここら辺でマニュアルを見ておくべきだろう。

           option name
                   Set a kernel option.  Kernel options may take either the
                   form NAME or the form NAME=value.  These options are passed
                   to the compiler with the -D flag.

           maxusers number
                   Required.  Used to size various system tables and maximum
                   operating conditions in an approximate fashion.  Multiple
                   instances of this keyword may be specified.  The number
                   provided in the last instance will be used, and warnings
                   will be printed for each duplicate value.  This is
                   convenient when used with the include directive.

SEE ALSO
     options(4), bsd.re-config(5), files.conf(5), boot.conf(8), boot_config(8)

     The SYNOPSIS portion of each device in section 4 of the manual.

     S. J. Leffler and M. J. Karels, “Building 4.4 BSD Systems with Config”,
     4.4BSD System Manager's Manual (SMM).

options(4)は大事だから見ておけ。カーネルの機能の説明がされている。それからfiles.conf(5)だな。こちらはデバイスがらみの説明。マシンのアーキテクチャ特有の設定が説明されてる。

この〜木なんの木、気になる木。ったら、どこかの会社のCMソングだったけど、カーネル屋さんも無視出来無い木。そう、デバイス・ツリーね。ああ、クリスマス・ツリーは、小学生の希望の木だ。先程散歩で、少学校の前を通ったら、生徒さんと先生が一生懸命に飾りつけしてた。

vbox$ cd /sys/arch/riscv64/conf/
vbox$ lv files.riscv64
#
# mainbus
#
define  mainbus {}
device  mainbus: fdt
attach  mainbus at root
file    arch/riscv64/dev/mainbus.c              mainbus
#
# simplebus
#
device  simplebus: fdt
attach  simplebus at fdt
file    arch/riscv64/dev/simplebus.c            simplebus

# Machine-independent FDT drivers
include "dev/fdt/files.fdt"

mainbus(別名fdt)は、親がてっぺんのrootです。それは、mainbusって言う定義が有れば、mainbus.cを取込ますって言う意味の定義なんだな。こんな表現をdot図にしたら面白いかもしれないな。

また、こんな風にincludeも出来るんで、追跡してみる。

vbox$ lv /usr/src/sys/dev/fdt/files.fdt
   :
# Google Goldfish real-time clock
device  gfrtc
attach  gfrtc at fdt
file    dev/fdt/gfrtc.c                 gfrtc

出てきましたねぇ。前回やったデバイスが。

これらが、Makefileを作る為の大本になるネタ帳だ(システムワイドな設定は、sys/confの下に有る)。

これらから種々選択して、おれ様カーネルを作る。その仕様ファイルが上の例だとSEEINGだ。GENERICは、これなら無事にインストール出来るでしょっと言う、最大規模っぽい仕様。

machine         riscv64
include         "../../../conf/GENERIC"
 :
# mainbus
mainbus0        at root

# cpu0
cpu0            at mainbus0
intc0           at cpu0

virtio*         at fdt?
virtio*         at pci?
vio*            at virtio?      # Network

syscon*         at fdt? early 1
gfrtc*          at fdt?

こんな風に、選りすぐっているのね。これを減らせば、より小さいカーネルが作成出来る。今度、最小のカーネル作りに挑戦してみるかな。

configの成果物

出来上がったカーネル作成環境を見ておく。

ob$ tail -3 KERNEL/Makefile
.PHONY: config
config:
        cd /home/sakae && config -s /sys -b /home/sakae/KERNEL SEEING

普通のconfigureでも、ログにこれ相当が有ったような気がするぞ。KERNELってdirは、存在しなかったら、勝手に作成されるんで、ファイルが散らかる事は無い。

Makefileが作られるのは当然の事。machineってリンクが作成されてた。マシン依存なincludeエリアへのリンク。よくやる手だな。

ヘッダーファイルが93ヶ作成されてた。いずれも行数が数行のもの。

ob$ wc *.h | sort -nr | head
     130     390    2282 total
      11      33     232 rasops_glue.h
       7      21     136 lpt.h
       5      15      94 com.h
       5      15      94 ahci.h
       3       9      71 wsfont_glue.h
       3       9      57 sti.h
       3       9      53 vga.h
       3       9      50 cy.h
       3       9      48 uhid.h
ob$ cat com.h
#define NCOM    1
#define NCOM    1
#define NCOM_CARDBUS    0
#define NCOM_GSC        0
#define NCOM_ISAPNP     0

代表的なcom.hをみたけど、#defineだけだった。他も同様だ。どういう基準でこういうのを作っているのだろう? このdefineが/sysの中のソース中に存在するかと思うと、そうでもない奴が有ったりするぞ。

Makefile

大事なレシピなんで、少し大事に見てく。 冒頭を飾るのは、-Dの定義類。仕様書のopeionが、これに変換される。機能を有効にするか否かの設定。

ob$ head -1 Makefile | tr ' ' '\n'
IDENT=-DDDB
-DDIAGNOSTIC
-DKTRACE
-DACCOUNTING
 :
-DWSDISPLAY_COMPAT_RAWKBD
-DWSDISPLAY_DEFAULTSCREENS="6"

ソース中で、#ifdef KTRACE みたいな形で使われ、ソースを有効/無効にしてる。options(4)に個別の説明が有る。強引にtrで展開しちゃったけど、optionsってファイルが出来ているんで、それを見るのがスマートだ。

次は、PARAM=-DMAXUSERS=80 だ。かの昔、FreeBSDで独自カーネルを作る時、これを小くしたらいいだろうって事でやってみた事がある。どう反映されるの? Makefile中では、

INCLUDES=       -nostdinc -I$S -I${.OBJDIR} -I$S/arch \
                -I$S/dev/pci/drm/include \
                -I$S/dev/pci/drm/include/uapi
CPPFLAGS=       ${INCLUDES} ${IDENT} ${PARAM} -D_KERNEL -D__${_mach}__ -MD -MP

こんな風に使われている。CPPって事は、ヘッダーがらみなのかな? 取り敢えず、ガサ入れってか家捜し。

ob$ grep MAXUSERS -rI .
./conf/param.c: * Compiled with -DHZ=xx -DMAXUSERS=xx
./conf/param.c:#define  NPROCESS (30 + 16 * MAXUSERS)
./conf/param.c:int      maxfiles = 5 * (NPROCESS + MAXUSERS) + 80;

ふーん、お一人様平均でプロセスは16ヶぐらいは使うでしょう。ファイル数は、それを元に計算しますとな。その他のパラメータも、これらを元に算出してる。これで、長年の疑問が氷解したな。

参考までにparam.cを見ると

int     maxprocess = NPROCESS;
int     maxthread = 2 * NPROCESS;
int     maxfiles = 5 * (NPROCESS + MAXUSERS) + 80;

こんな、関数の外側で定義されてる変数が出て来る。これって、いわゆるカーネル変数じゃん。運が好ければ、後で変更出来るはず。ちょいと確かめてみる。

vbox$ sysctl -a | grep maxprocess
vbox$ sysctl -a | grep utc_offset
kern.utc_offset=540
vbox$ sysctl -a | grep maxthread
kern.maxthread=2620
vbox$ sysctl -a | grep maxfiles
kern.maxfiles=7030

勝手に変更すると痛い目に遭いそうなものも混ってますが。。。

trace open

configがどんなファイルをオープンするのだろう? ktraceは実ファイル名が出てこないので除外。そうするとgdbなスクリプトだなあ。こんなスクリプトを用意した。

b open
commands
  silent
  bt 1
  cont
end
run -b /tmp/KERNEL -s /usr/src/sys SEEING
quit

何度もトライするので、/tmpな環境です。起動は、

vbox$ gdb -q /home/sakae/config -x script >LOG

gdbレディーなconfigが/home/sakae/configとして用意してあるので、それを利用。結果はat 行が不要なのでオミットする。

vbox$ cat LOG | sed '/at /d' | lv
#0  _libc_open_cancel (
    path=0x43e5a680 "/usr/src/sys/arch/riscv64/conf/files.riscv64", flags=0)
#0  _libc_open_cancel (path=0x43e55340 "/dev/null", flags=0)
#0  _libc_open_cancel (path=0x43e7a680 "/usr/src/sys/conf/files", flags=0)
#0  _libc_open_cancel (path=0x43e7ab00 "/usr/src/sys/dev/pv/files.pv", flags=0)
#0  _libc_open_cancel (path=0x43e49400 "/usr/src/sys/dev/hid/files.hid",
 :
#0  _libc_open_cancel (path=0x6e1b7d00 "/usr/src/sys/dev/usb/files.usb",
#0  _libc_open_cancel (path=0x6e1a8280 "/usr/src/sys/conf/GENERIC", flags=0)
#0  _libc_open_cancel (
    path=0x4f704e00 "/usr/src/sys/arch/riscv64/conf/Makefile.riscv64", flags=0)
#0  _libc_open_cancel (path=0x15d6b45b "Makefile", flags=1537)
#0  _libc_open_cancel (path=0xcf7db1ac "drm.h", flags=0)
#0  _libc_open_cancel (path=0xcf7db1ac "vga.h", flags=0)
 :
#0  _libc_open_cancel (path=0xcf7db1ac "ulpt.h", flags=0)
#0  _libc_open_cancel (path=0xcf7db1ac "athn_usb.h", flags=0)
#0  _libc_open_cancel (path=0x15d6c629 "ioconf.c", flags=1537)
#0  _libc_open_cancel (path=0xcf7db938 "ioconf.incl.riscv64", flags=0)
#0  _libc_open_cancel (path=0x15d6aa44 "options", flags=0)
#0  _libc_open_cancel (path=0x15d6aa44 "options", flags=1538)

最小のカーネル

次は、最小のカーネルをどうやって作るかだ。それには、現状のデバイスツリーの探索結果が格納されているgmesgを調べる必要が有るな。

dmesg

現在のriscvのdmesg

mainbus0 at root: riscv-virtio,qemu
cpu0 at mainbus0: vendor 0 arch 70100 imp 70100 rv64imafdch_zicsr_zifencei_zba_zLa\M-<  %\M-c[\M^V\M-$#\M-Z\^B\M-@\M^?\M^?\M^?
intc0 at cpu0
"fw-cfg" at mainbus0 not configured
"flash" at mainbus0 not configured
simplebus0 at mainbus0: "platform"
simplebus1 at mainbus0: "soc"
syscon0 at simplebus1: "poweroff"
syscon1 at simplebus1: "reboot"
syscon2 at simplebus1: "test"
plic0 at simplebus1
gfrtc0 at simplebus1
com0 at simplebus1: ns16550, no working fifo
com0: console
pciecam0 at simplebus1
pci0 at pciecam0 "Red Hat Host" rev 0x00 at pci0 dev 0 function 0 not configured
virtio0 at simplebus1: Virtio Block Device
vioblk0 at virtio0
scsibus0 at vioblk0: 1 targets
sd0 at scsibus0 targ 0 lun 0: <VirtIO, Block Device, >
sd0: 4096MB, 512 bytes/sector, 8388608 sectors
virtio1 at simplebus1: Virtio Block Device
vioblk1 at virtio1
scsibus1 at vioblk1: 1 targets
sd1 at scsibus1 targ 0 lun 0: <VirtIO, Block Device, >
sd1: 2048MB, 512 bytes/sector, 4194304 sectors
virtio2 at simplebus1: Virtio Network Device
vio0 at virtio2: address 52:54:00:12:34:56
virtio3 at simplebus1: Virtio Unknown (0) Device
 :
virtio7 at simplebus1: Virtio Unknown (0) Device
"clint" at simplebus1 not configured
vscsi0 at root
scsibus2 at vscsi0: 256 targets
softraid0 at root
scsibus3 at softraid0: 256 targets

関係しそうな物を抜き出してみた。

dmassage

昔fuguitaで見付たawk版のもの、 dmassage

portsにも有ったんで有名なのか(FreeBSDには無かったぞ)、perl版数のやつ。perl版数で試す。dmesgのログは上の奴をriscDMってした。

vbox$ dmassage -d riscvDM  -t
root
 |-mainbus0
 |  |-cpu0
 |  |  \-intc0
 |  |-simplebus0
 |  \-simplebus1
 |     |-com0
 |     |-gfrtc0
 |     |-pciecam0
 |     |  \-pci0
 |     |-plic0
 |     |-syscon0
 |     |-syscon1
 |     |-syscon2
 |     |-virtio0
 |     |  \-vioblk0
 |     |     \-scsibus0
 |     |        \-sd0
 |     |-virtio1
 |     |  \-vioblk1
 |     |     \-scsibus1
 |     |        \-sd1
 |     |-virtio2
 |     |  \-vio0
 |     |-virtio3
 |     \-virtio7
 |-softraid0
 |  \-scsibus3
 \-vscsi0
    \-scsibus2

かっちょいい!!

riscv$ pkg_info dmassage
Information for inst:dmassage-0.6p4

Comment:
dmesg parser

Description:
dmassage parses your system's dmesg to learn which devices are
successfully detected by the kernel.  This information can be used for
three purposes: to make the kernel boot faster, to help build a smaller
kernel or to show all the devices in a tree-like hierarchy.

Maintainer: The OpenBSD ports mailing-list <ports@openbsd.org>

This year's Index

Home