WRAP

WRAP

Advnet Calendar 2022 で盛り上りをみせているみたいだけど、それは全部揃ってから見る事にして、今はラッピングですよ。前回、gettimeofdayがラッピングされてて、それはマシンの差を吸収する為って、いいかげんな結論を出しちゃった。

でも、包んであるって事は、きっと大事な物が隠されているに違いない。野生の感が働くか? ちょいとコードを追跡してみたら、止めたい所が出て来た。

riscv$ gdb -q date
Reading symbols from date...
(gdb) b rdtime
Breakpoint 1 at 0x17cc4: file /usr/src/lib/libc/arch/riscv64/gen/usertc.c, line
27.
(gdb) r
Starting program: /tmp/date

Breakpoint 1, rdtime () at /usr/src/lib/libc/arch/riscv64/gen/usertc.c:27
27              __asm volatile("rdtime %0" : "=r"(ret));
(gdb) bt
#0  rdtime () at /usr/src/lib/libc/arch/riscv64/gen/usertc.c:27
#1  tc_get_timecount (tk=<optimized out>, tc=0x3fffff66e4)
    at /usr/src/lib/libc/arch/riscv64/gen/usertc.c:37
#2  0x00000001773199b8 in tc_delta (tk=0x23b05e000, delta=<optimized out>)
    at /usr/src/lib/libc/sys/microtime.c:34
#3  bintime (bt=0x3fffff6720, tk=0x23b05e000)
    at /usr/src/lib/libc/sys/microtime.c:85
#4  0x0000000177319930 in _microtime (tvp=0x3fffff6770, tk=<optimized out>)
    at /usr/src/lib/libc/sys/microtime.c:101
#5  0x000000017731774a in _libc_gettimeofday_wrap (tp=0x3fffff6770, tzp=0x0)
    at /usr/src/lib/libc/sys/w_gettimeofday.c:31
#6  0x000000017730cc02 in _libc_time (t=0x177324ad0 <tval>)
    at /usr/src/lib/libc/gen/time.c:39
#7  0x000000017730a33a in main (argc=0, argv=0x3fffff6c60) at date.c:98

なんとlibcにも機種依存のコードが有るのね。想像もつかなかったぞ。折角なので、逆アセしてみた。

   0x0000000177319cb8 <+0>:     lw      a2,76(a0)
   0x0000000177319cba <+2>:     li      a3,1
   0x0000000177319cbc <+4>:     li      a0,-1
   0x0000000177319cbe <+6>:     bne     a2,a3,0x177319cca <tc_get_timecount+18>
   0x0000000177319cc2 <+10>:    li      a0,0
=> 0x0000000177319cc4 <+12>:    rdtime  a2
   0x0000000177319cc8 <+16>:    sw      a2,0(a1)
   0x0000000177319cca <+18>:    sext.w  a0,a0
   0x0000000177319ccc <+20>:    ret
End of assembler dump.

ちょいと異色なオペコードが出ていたぞ。なんか特注っぽい臭いがする。

(gdb) si
tc_get_timecount (tk=<optimized out>, tc=0x3fffff66e4)
    at /usr/src/lib/libc/arch/riscv64/gen/usertc.c:37
37                      *tc = rdtime();
(gdb) n
(gdb) p/x *tc
$3 = 0x6ea6846d
(gdb) c
 :
(gdb) p/x *tc
$5 = 0x99de3abd

アクセスの度に値がインクリメントされてる。ここはもう、取扱説明書の出番だな。

ぐだぐだ低レベルプログラミング(21) GD32VF103のサイクルカウンタ辺の実装

The RISC-V Instruction Set Manual Volume I: User-Level ISA page 22

RISC-V user-level ISA by takeoka 超要約バージョン

RISC-V Assembly Programmer's Manual

どうもCPUに内蔵されてるカウンターって事だな。

myprog

一応、自前のプログラムも作ってみた。いわゆる純粋培養ね。まあ、libcのそれをパクッただけだけど。

#include <sys/types.h>
#include <stdio.h>

static inline u_int
rdtime(void) {
        uint64_t ret;
        __asm volatile("rdtime %0" : "=r"(ret));
        return ret;
}

int main(){
        printf("%u\n", rdtime());
        printf("%u\n", rdtime());
}

何回か実行。確かにカウンターだ。それより驚くべき事は、システムコールしなくても、CPUの中のカウンターをアクセス出来る事。こんな事を許して委員会?

riscv$ ./a.out
3705945385
3706296898
riscv$ ./a.out
3761925097
3762262483

at PCAT

古典的なマシンって事で、10年前のThinkPad で動いてるFreeBSDのハードウェア・プロービングの結果。

[sakae@fb ~]$ dmesg | grep timer
Event timer "LAPIC" quality 100
Event timer "HPET" frequency 14318180 Hz quality 450
Event timer "HPET1" frequency 14318180 Hz quality 440
Event timer "HPET2" frequency 14318180 Hz quality 440
Event timer "HPET3" frequency 14318180 Hz quality 440
Event timer "RTC" frequency 32768 Hz quality 0
attimer0: <AT timer> port 0x40-0x43,0x50-0x53 irq 0 on acpi0
Event timer "i8254" frequency 1193182 Hz quality 100
acpi_timer0: <24-bit timer at 3.579545MHz> port 0x408-0x40b on acpi0

ユーザーランドから、手軽に触れそうにないな。しっかり周辺機器扱いになってるし、CPU内蔵も、アクセスするには、それなりの作法が必要っぽい。

others WRAP

ラッピングされてるのは、他に無いか、少し探りをいれてみた。やっぱりボチボチでんなあ。

ob$ cd /usr/src/lib
ob$ grep DEF_WRAP -rIl .
./libc/include/DETAILS
./libc/include/README
./libc/include/namespace.h
./libc/sys/ptrace.c
./libc/sys/w_clock_gettime.c
./libc/sys/w_fork.c
./libc/sys/w_gettimeofday.c
./libc/sys/w_sigaction.c
./libc/sys/w_sigprocmask.c
./libc/sys/w_vfork.c

ついでに、riscv64と名のつくarchの場所も調べておく。

ob$ cd /usr/src
ob$ find . -type d -name riscv64
./distrib/notes/riscv64
./distrib/riscv64
./lib/csu/riscv64
./lib/libc/arch/riscv64
./lib/libcrypto/arch/riscv64
./lib/libm/arch/riscv64
./libexec/ld.so/riscv64
./sys/arch/riscv64
./sys/arch/riscv64/riscv64
./sys/stand/efi/include/riscv64

kernel side

kern/init_main.c/initclocks() -> kern/kern_clock.c/cpu_initclocks()

arch/riscv64/riscv64//clock.c

cpu_initclocks(void)
{
        tb_timecounter.tc_frequency = tb_freq;
        tc_init(&tb_timecounter);

        tick_increment = tb_freq / hz;
         :

ハード屋はハードの諸元が、ついつい気になる。 tb_freq ってどれぐらい? machdep.cに定義があった。 uint64_t tb_freq = 1000000; ですって。素直に解釈すると1MHzだから、1usでカウントアップするんだな。

そして、hz は param.c で、100が設定されている。これ、糞石システムでは50だったか60だったように思うぞ。

そして、このファイルにも、rdtime()が定義されてた。カーネル側のやつね。riscv64/include/cpufunc.hに定義されてた。

#define rdcycle()                       csr_read(cycle)
#define rdtime()                        csr_read(time)
#define rdinstret()                     csr_read(instret)
#define rdhpmcounter(n)                 csr_read(hpmcounter##n)

この定義に出て来る ## は、コンパイラーの工程プリプロセッサーの命令で、文字列の結合だ。 また、 csr_read() は、riscvreg.h

#define csr_read(csr)                                                   \
({      u_long val;                                                     \
        __asm volatile("csrr %0, " #csr : "=r" (val));          \
        val;                                                            \
})

に定義されてる。libc側の定義と比べると、より原理的だな。

boottime

kern_tc.c に面白い構造体が定義されてた。

struct timehands {
        /* These fields must be initialized by the driver. */
        struct timecounter      *th_counter;            /* [W] */
        int64_t                 th_adjtimedelta;        /* [T,W] */
        struct bintime          th_next_ntp_update;     /* [T,W] */
        int64_t                 th_adjustment;          /* [W] */
        u_int64_t               th_scale;               /* [W] */
        u_int                   th_offset_count;        /* [W] */
        struct bintime          th_boottime;            /* [T,W] */
        struct bintime          th_offset;              /* [W] */
        struct bintime          th_naptime;             /* [W] */
        struct timeval          th_microtime;           /* [W] */
        struct timespec         th_nanotime;            /* [W] */
        /* Fields not to be copied in tc_windup start with th_generation. */
        volatile u_int          th_generation;          /* [W] */
        struct timehands        *th_next;               /* [I] */
};

この中にある、 th_boottime って、いつセットされるんだろう? その前に bintimeって型は? 型はきっとヘッダーで説明されてるはず。time.h。

/* Time expressed as seconds and fractions of a second + operations on it. */
=>ruct bintime {
        time_t  sec;
        uint64_t frac;
};

/*
 * Functions for looking at our clocks: [get]{bin,nano,micro}[boot|up]time()
 *
 * Functions without the "get" prefix returns the best timestamp
 * we can produce in the given format.
 *
 * "bin"   == struct bintime  == seconds + 64 bit fraction of seconds.
 * "nano"  == struct timespec == seconds + nanoseconds.
 * "micro" == struct timeval  == seconds + microseconds.
 *
 * Functions containing "up" returns time relative to boot and
 * should be used for calculating time intervals.
 *
 * Functions containing "boot" return the GMT time at which the
 * system booted.
 *
 * Functions with just "time" return the current GMT time.
    :

色々な精度に対応出来るようになってるんだな。

じゃ、更新されるタイミングを捉える為、watch th0.th_boottime しておく。それは、gdbが下記のように解釈するんだね。なおtimehands の型を使うグローバル変数に、th1ってのもあるけど、th0っとリングになってる。この使い分けがいまいち不明。

(gdb) info breakpoints
Num     Type           Disp Enb Address            What
2       hw watchpoint  keep y                      th0.th_boottime
        breakpoint already hit 1 time

で、実行結果。

(gdb) bt
#0  0xffffffc000346226 in tc_windup (new_boottime=0xffffffc000805bd8, new_offset=0x0, new_adjtimedelta=0xffffffc000805bd0) at\
 /usr/src/sys/kern/kern_tc.c:712
#1  0xffffffc000345fd8 in tc_setrealtimeclock (ts=0xffffffc000805ca0) at /usr/src/sys/kern/kern_tc.c:532
#2  0xffffffc0003465ee in tc_setclock (ts=0xffffffc000805ca0) at /usr/src/sys/kern/kern_tc.c:567
#3  0xffffffc00021fcb8 in inittodr (base=1669878550) at /usr/src/sys/kern/kern_time.c:904
#4  0xffffffc000349004 in ffs_mountroot () at /usr/src/sys/ufs/ffs/ffs_vfsops.c:193
#5  0xffffffc00035d88a in dk_mountroot () at /usr/src/sys/kern/subr_disk.c:1346
#6  0xffffffc00030af30 in main (framep=<optimized out>) at /usr/src/sys/kern/init_main.c:456

面白い所から、呼び出されているな。

trans

ソースを見ていると、コメントで重要な事が書いてある。すらすら読めればいいんだけど、そこは、そう察してください。そこで、 google AI を使うか、 https://www.deepl.com/translator とかのお世話になる。

そんな時、冒頭にあるコメントマークが、何となく邪魔。マークを外したやつを与えたい。

そんな時は、emacsの矩形編集ですよ。 C-x r r で、矩形を選択。それをレジスターに保存、するんで任意の一文字でレジスターを与える。次は、新しいバッファーで、 C-x r i して、レジスターからペーストする。

また、emacsなら、ESC-; で、指定した部分を comment/uncomment 出来る。じゃ、viは? 我辞書にはそんな便利なやつは乗っていなかった。vimでも Vimのコメントアウトプラグインtcommentを試す な状況みたい。それより問題なのは、これらを使うと原本のソースを変更しちゃうって事だな。そんなの許しがたいぞ。しょうがないからスクリプトを作ろう。

#!/bin/sh
# delete comment mark, Usage: paste comment line's then C-d
cat               |
sed -e 's!/\*!!'  |
sed -e 's!\*/!!'  |
sed -e 's!\*!!'

まんまのスクリプトである。適当な場所にdcmとかの名前で配置。OpenBSDのソースの一節を実験台にしてみた。

sakae@deb:~$ dcm
/*
 * Return the difference between the timehands' counter value now and what
 * was when we copied it to the timehands' offset_count.
 */
## コメントをり貼付けてから、C-d して、入力の終了を教えてあげる。
  Return the difference between the timehands' counter value now and what
  was when we copied it to the timehands' offset_count.

boot time

riscVを起動したら、こんなメッセージが出てきた。

root on sd0a (781d4a4515da21ce.a) swap on sd0b dump on sd0b
WARNING: clock gained 4 days
WARNING: CHECK AND RESET THE DATE!
Automatic boot in progress: starting file system checks.

飲み屋のおねーちゃんみたいに、軽く避難された。

riscv$ sysctl -a | grep boot
kern.boottime=Tue Dec  6 06:30:31 2022

起動時間は、それなりに正しい。と言う事は、OSは自力で現在時刻を察知してるって事になる。その機能はどう実現してるの? 大疑問である。

riscv$ last -10
sakae     console                           Tue Dec 06 06:33   still logged in
reboot    ~                                 Tue Dec 06 06:31
shutdown  ~                                 Fri Dec 02 06:28
sakae     console                           Fri Dec 02 05:18 - shutdown  (01:09)

ちなみに、過去の訪問日時を確認してみた。ちゃんと指摘の通りであったぞ。ねーちゃんの記憶力は抜群だな。

SPDX-License-Identifier

久し振りに、こじまみつひろ さんの記事が上ってた。 第44回 linux-6.0とSPDX

SPDXなんていうまとめが有るとな。で、FreeBSDをうろうろしてたら、

timetc.h

/*-
 * SPDX-License-Identifier: Beerware
 *
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * <phk@FreeBSD.org> wrote this file.  As long as you retain this notice you
 * can do whatever you want with this stuff. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
 * ----------------------------------------------------------------------------
 *
 :

こんな楽しいライセンスを発見。日本人のオイラーなら、Sake だな。Sake駆動でソフト業界をうろうろ。それを体現したRubyの人がいたけど、お元気かしら?

/*-
 * `struct timecounter' is the interface between the hardware which implements
 * a timecounter and the MI code which uses this to keep track of time.
 *
 * A timecounter is a binary counter which has two properties:
 *    * it runs at a fixed, known frequency.
 *    * it has sufficient bits to not roll over in less than approximately
 *      max(2 msec, 2/HZ seconds).  (The value 2 here is really 1 + delta,
 *      for some indeterminate value of delta.)
 */

dcmの出番ですよ、と。


This year's Index

Home