Ubuntu18.04でdpg0100のビルド

### これは何?

Ubuntu18.04で[PEX-361316](http://www.interface.co.jp/catalog/prdc.asp?name=PEX-361316)を利用したい。
いくつかモジュールを組み込む必要があるが、カーネルが新しいため、ビルドが必要。ビルドを通す際の記録。
いくつかビルドを通す必要があるが、長くなるため、`dpg0100`のみを取り上げます。

### ビルドを通すまで

とりあえずmake

```
$ cd ~/work/PEX-361316/common/dpg0100/src
$ make
cc1: fatal error: /lib/modules/5.0.0-37-generic/build/include/linux/version.h: そのようなファイルやディレクトリはありません
compilation terminated.
gcc -DMODULE -D__KERNEL__ -DEXPORT_SYMTAB -Wall -Wstrict-prototypes -O -fomit-frame-pointer -pipe -ffreestanding -fno-strict-aliasing -fno-common -I/lib/modules/5.0.0-37-generic/build/include -I/lib/modules/5.0.0-37-generic/build/arch/x86/include -I/lib/modules/5.0.0-37-generic/build/include/asm/mach-default -I/lib/modules/5.0.0-37-generic/build/arch/x86/include/asm/mach-generic  -mcmodel=kernel -mno-red-zone -include /lib/modules/5.0.0-37-generic/build/include/generated/autoconf.h -D__SMP__ -DEXPORT_SYMTAB -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(dpg0100)" -D"KBUILD_MODNAME=KBUILD_STR(dpg0100)"   -c -o dpg0100.o dpg0100.c
cc1: error: code model kernel does not support PIC mode
<ビルトイン>: recipe for target 'dpg0100.o' failed
make: *** [dpg0100.o] Error 1
```

`version.h`が無いため、生成する。

```
$ sudo apt install linux-headers-`uname -r`
$ cd /usr/src/linux-headers-5.0.0-37/
$ sudo make include/generated/uapi/linux/version.h
$ sudo ln -s /usr/src/linux-headers-5.0.0-37/include/generated/uapi/linux/version.h /lib/modules/5.0.0-37-generic/build/include/linux/version.h
$ cd ~/work/PEX-361316/common/dpg0100/src
$ make
gcc -DMODULE -D__KERNEL__ -DEXPORT_SYMTAB -Wall -Wstrict-prototypes -O -fomit-frame-pointer -pipe -ffreestanding -fno-strict-aliasing -fno-common -I/lib/modules/5.0.0-37-generic/build/include -I/lib/modules/5.0.0-37-generic/build/arch/x86/include -I/lib/modules/5.0.0-37-generic/build/include/asm/mach-default -I/lib/modules/5.0.0-37-generic/build/arch/x86/include/asm/mach-generic  -mcmodel=kernel -mno-red-zone -include /lib/modules/5.0.0-37-generic/build/include/generated/autoconf.h -D__SMP__ -DEXPORT_SYMTAB -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(dpg0100)" -D"KBUILD_MODNAME=KBUILD_STR(dpg0100)"   -c -o dpg0100.o dpg0100.c
cc1: error: code model kernel does not support PIC mode
<ビルトイン>: recipe for target 'dpg0100.o' failed
make: *** [dpg0100.o] Error 1
```

以下を`/lib/modules/5.0.0-37-generic/build/Makefile`内の`all: vmlinux`の下に追加。

```Makefile
# force no-pie for distro compilers that enable pie by default
KBUILD_CFLAGS += $(call cc-option, -fno-pie) 
KBUILD_CFLAGS += $(call cc-option, -no-pie)
KBUILD_AFLAGS += $(call cc-option, -fno-pie)
KBUILD_CPPFLAGS += $(call cc-option, -fno-pie)
```

あわせて`dpg0100/src/Makefile`も更新。

```Makefile
CFLAGS += -I$(INCLUDEDIR)
CFLAGS += -fno-pie # 追加
```

```
$ make
gcc -DMODULE -D__KERNEL__ -DEXPORT_SYMTAB -Wall -Wstrict-prototypes -O -fomit-frame-pointer -pipe -ffreestanding -fno-strict-aliasing -fno-common -I/lib/modules/5.0.0-37-generic/build/include -fno-pie -I/lib/modules/5.0.0-37-generic/build/arch/x86/include -I/lib/modules/5.0.0-37-generic/build/include/asm/mach-default -I/lib/modules/5.0.0-37-generic/build/arch/x86/include/asm/mach-generic  -mcmodel=kernel -mno-red-zone -include /lib/modules/5.0.0-37-generic/build/include/generated/autoconf.h -D__SMP__ -DEXPORT_SYMTAB -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(dpg0100)" -D"KBUILD_MODNAME=KBUILD_STR(dpg0100)"   -c -o dpg0100.o dpg0100.c
In file included from /lib/modules/5.0.0-37-generic/build/arch/x86/include/asm/cpufeature.h:5:0,
…
                 from dpg0100.c:9:
/lib/modules/5.0.0-37-generic/build/arch/x86/include/asm/processor.h:412:15: error: missing binary operator before token "("
 #if IS_ENABLED(CONFIG_KVM)
               ^
…
/lib/modules/5.0.0-37-generic/build/include/linux/mmzone.h: At top level:
/lib/modules/5.0.0-37-generic/build/include/linux/mmzone.h:147:15: error: missing binary operator before token "("
 #if IS_ENABLED(CONFIG_ZSMALLOC)
               ^
In file included from /lib/modules/5.0.0-37-generic/build/arch/x86/include/asm/realmode.h:15:0,
…
                 from /lib/modules/5.0.0-37-generic/build/include/linux/module.h:13,
                 from dpg0100.c:9:
/lib/modules/5.0.0-37-generic/build/arch/x86/include/asm/io.h:44:10: fatal error: asm/early_ioremap.h: そのようなファイルやディレクトリはありません
 #include <asm/early_ioremap.h>
          ^~~~~~~~~~~~~~~~~~~~~
compilation terminated.
<ビルトイン>: recipe for target 'dpg0100.o' failed
make: *** [dpg0100.o] Error 1
```

`IS_ENABLED`マクロは`kconfig.h`にあるようなので、インクルード対象に追加。

```dpg0100.c
#include <linux/kconfig.h> // 追加
```

ヘッダファイルが見つからないのは、`version.h`と同様にリンクすれば解決する。

```
$ find /usr/src/linux-headers-5.0.0-37-generic/ -name '*ioremap*'
/usr/src/linux-headers-5.0.0-37-generic/arch/x86/include/generated/asm/early_ioremap.h
/usr/src/linux-headers-5.0.0-37-generic/include/config/have/ioremap
/usr/src/linux-headers-5.0.0-37-generic/include/config/generic/early/ioremap.h
$ sudo ln -s /usr/src/linux-headers-5.0.0-37-generic/arch/x86/include/generated/asm/early_ioremap.h /lib/modules/5.0.0-37-generic/build/arch/x86/include/asm/early_ioremap.h
$ # 以下makeやfindするのは省略
$ sudo ln -s /usr/src/linux-headers-5.0.0-37-generic/arch/x86/include/generated/asm/unistd_64_x32.h /lib/modules/5.0.0-37-generic/build/arch/x86/include/asm/unistd_64_x32.h
$ sudo ln -s /usr/src/linux-headers-5.0.0-37-generic/arch/x86/include/generated/asm/unistd_32_ia32.h /lib/modules/5.0.0-37-generic/build/arch/x86/include/asm/unistd_32_ia32.h
$ sudo ln -s /usr/src/linux-headers-5.0.0-37/include/uapi/linux/rseq.h /lib/modules/5.0.0-37-generic/build/include/linux/rseq.h
$ make
…
/lib/modules/5.0.0-37-generic/build/include/linux/signal_types.h: At top level:
/lib/modules/5.0.0-37-generic/build/include/linux/signal_types.h:13:2: error: expected specifier-qualifier-list before ‘__SIGINFO’
  __SIGINFO;
  ^~~~~~~~~
dpg0100.c:20:10: fatal error: asm/system.h: そのようなファイルやディレクトリはありません
 #include <asm/system.h>
          ^~~~~~~~~~~~~~
compilation terminated.
<ビルトイン>: recipe for target 'dpg0100.o' failed
make: *** [dpg0100.o] Error 1
```

`__SIGINFO`を定義しているヘッダファイルをインクルードできていない。
`siginfo.h`を差し替えて対応。

```
$ sudo mv /usr/include/asm-generic/siginfo.h /usr/include/asm-generic/siginfo.h.bak
$ sudo ln -s /usr/src/linux-headers-5.0.0-37/include/uapi/asm-generic/siginfo.h /usr/include/asm-generic/siginfo.h
```

`<asm/system.h>`は現在、いくつかのヘッダファイルに分割されているらしい。
とりあえずコメントに変更し、`make`。いくつかエラーが出てきたため、修正。

```dpg0100.c
//#include <asm/system.h> // コメントに

//return MINOR(((struct file *)file)->f_dentry->d_inode->i_rdev);
return MINOR(((struct file *)file)->f_path.dentry->d_inode->i_rdev);

/*
ret = get_user_pages(current, current->mm, (Buffer & PAGE_MASK),
    mdl->NumberOfPages, Direction, 1,
    (struct page **)mdl->PageList, NULL);
*/
if(Direction == 1) {
    ret = get_user_pages((Buffer & PAGE_MASK), mdl->NumberOfPages,
                     FOLL_FORCE | FOLL_WRITE, (struct page **)mdl->PageList, NULL);
} else {
    ret = get_user_pages((Buffer & PAGE_MASK), mdl->NumberOfPages,
                     FOLL_FORCE, (struct page **)mdl->PageList, NULL);
}

//page_cache_release((struct page *)mdl->PageList[i]);
put_page((struct page *)mdl->PageList[i]);
// もう1箇所同様に修正
```

`dpg0100.c`のコンパイルが通るようになった。

```
$ make
In file included from /lib/modules/5.0.0-37-generic/build/include/linux/mm_types.h:5:0,
                 from /lib/modules/5.0.0-37-generic/build/arch/x86/include/asm/vdso.h:11,
                 from /lib/modules/5.0.0-37-generic/build/arch/x86/include/asm/elf.h:77,
                 from /lib/modules/5.0.0-37-generic/build/include/linux/elf.h:5,
                 from /lib/modules/5.0.0-37-generic/build/include/linux/elfnote.h:62,
                 from /lib/modules/5.0.0-37-generic/build/include/linux/build-salt.h:4,
                 from dpg0100.mod.c:1:
/lib/modules/5.0.0-37-generic/build/include/linux/mm_types.h: At top level:
/lib/modules/5.0.0-37-generic/build/include/linux/mm_types_task.h:24:13: error: missing binary operator before token "("
   IS_ENABLED(CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK))
             ^
…
/lib/modules/5.0.0-37-generic/build/arch/x86/include/asm/irq_vectors.h:102:15: error: missing binary operator before token "("
 #if IS_ENABLED(CONFIG_HYPERV)
               ^
Makefile:309: recipe for target 'dpg0100.mod.o' failed
make: *** [dpg0100.mod.o] Error 1

```

`dpg0100.mod.c`が生成されたが、ビルドが通らないため、`dpg0100.c`と同じように対処。

```dpg0100.mod.c
#include <linux/kconfig.h> // 追加
```

```
$ make
…
ld -m elf_x86_64 -r -o dpg0100.ko dpg0100.o dpg0100.mod.o
$ sudo make install
mkdir -p /lib/modules/5.0.0-37-generic/misc
cp -f dpg0100.ko /lib/modules/5.0.0-37-generic/misc
$
```

`dpg0100`のビルドができました。

### カーネル 5.0.37 から 5.3.26 への対応

カーネルの変化でビルドの通し方がちょっと変わった。
以下以外は5.0.37と同じです。

```
$ sudo ln -s /usr/src/linux-headers-5.3.0-26/include/uapi/linux/time_types.h /lib/modules/5.3.0-26-generic/build/include/linux/time_types.h
$ sudo ln -s /usr/src/linux-headers-5.3.0-26-generic/arch/x86/include/generated/asm/mmiowb.h /lib/modules/5.3.0-26-generic/build/arch/x86/include/asm/mmiowb.h
```

```dpg0100.c
#include <linux/kconfig.h>
typedef long long __kernel_time64_t; // 追加
```

```dpg0100.mod.c
#include <linux/kconfig.h> // 追加( 5.0.37の時も )
typedef long long __kernel_time64_t; // 追加
```

### ロード

```
$ sudo depmod -a
$ sudo modprobe dpg0100
modprobe: ERROR: could not insert 'dpg0100': Unknown symbol in module, or unknown parameter (see dmesg)
$ dmesg 
...
[ xxx.xxx] dpg0100: module license 'unspecified' taints kernel.
[ xxx.xxx] Disabling lock debugging due to kernel taint
[ xxx.xxx] dpg0100: module verification failed: signature and/or required key missing - tainting kernel
[ xxx.xxx] dpg0100: Unknown symbol init_timer (err -2)
[ xxx.xxx] dpg0100: Unknown symbol sev_enable_key (err -2)
$
```

うまくロードできない。
`init_timer`, `sev_enable_key`は今使用しているLinuxカーネルでは廃止されているらしい。

`init_timer`は`timer_setup`に差し替える。

```
/* コールバック関数、タイマーが切れたときに呼ばれる。 */
static void gpg_timer_fn(struct timer_list *t)
{
    return;
}
void gpg_init_timer(void *timer)
{
        //init_timer((struct timer_list *)timer);
        timer_setup((struct timer_list *)timer, gpg_timer_fn, 0);
        return;
}
```

`sev_enable_key`はコード内に現れない。

```
$ grep -R sev_enable_key ~/work/PEX-361316/
バイナリファイル xxx/PEX-361316/common/dpg0100/src/dpg0100.o に一致しました
バイナリファイル xxx/PEX-361316/common/dpg0100/src/dpg0100.ko に一致しました
$ grep -R sev_enable_key /lib/modules/5.0.0-37-generic/
...
/lib/modules/5.0.0-37-generic/build/arch/x86/include/asm/io.h:extern struct static_key_false sev_enable_key;
/lib/modules/5.0.0-37-generic/build/arch/x86/include/asm/io.h:	return static_branch_unlikely(&sev_enable_key);
...
バイナリファイル /lib/modules/5.0.0-37-generic/kernel/drivers/tty/isicom.ko に一致しました
バイナリファイル /lib/modules/5.0.0-37-generic/misc/dpg0100.ko に一致しました
$ sudo grep -R 'sev_enable_key' /boot/
/boot/System.map-5.0.0-37-generic:ffffffff82417c98 r __ksymtab_sev_enable_key
/boot/System.map-5.0.0-37-generic:ffffffff8241b46b r __kstrtab_sev_enable_key
/boot/System.map-5.0.0-37-generic:ffffffff82b57000 B sev_enable_key
/boot/System.map-5.0.0-23-generic:ffffffff823fd988 r __ksymtab_sev_enable_key
/boot/System.map-5.0.0-23-generic:ffffffff8240113e r __kstrtab_sev_enable_key
/boot/System.map-5.0.0-23-generic:ffffffff82b55000 B sev_enable_key
/boot/System.map-5.3.0-26-generic:ffffffff82454268 r __ksymtab_sev_enable_key
/boot/System.map-5.3.0-26-generic:ffffffff82457b4a r __kstrtab_sev_enable_key
/boot/System.map-5.3.0-26-generic:ffffffff82b75690 B sev_enable_key
```

カーネル内に`sev_enable_key`が含まれているようにみえる。`sev_enable_key`自体の定義は、`EXPORT_SYMBOL_GPL(sev_enable_key);`となっている。
そこで、試しに

```gpg0100.c
MODULE_AUTHOR("Interface Corporation <http://www.interface.co.jp>");
MODULE_LICENSE("GPL"); // 追加
```

としてみた。

```
$ make
$ sudo make install
$ sudo modprobe dpg0100
$ dmesg
[ xxx.xxx] dpg0100:Interface Common Device Driver (Ver2.80.13.00) loaded.
$ lsmod | grep dpg
dpg0100                45056  0
```

GPLライセンス有効とすれば、利用できるようです。
Wikipediaにある以下の記載に引っかかったようです。

> ライセンス問題
> Linuxカーネルコミュニティによれば、LKMはカーネルの二次的著作物(derivative works)[12]である。Linuxカーネル開発者はプロプライエタリなモジュールの配布を許容しているが、一部のカーネルシンボルはGPLのモジュールにのみ利用を許している。 

GPG-3100のreadme.txtを参照し、
PEX-361316を利用する目的での改変であること、'19年8月でサポートが切れている事から、`MODULE_LICENSE("GPL");`としても問題ないと前向きに解釈し、進めたいと思います。

**本記事を参考にやる方は自己責任でお願いいたします。(ライセンスの話も関わっているので、明記させていただきます。)**

### CP3100ロード時の問題対処

CP3100をロードすると、`Unknown symbol gpg_outl (err -2)`など、なぜか参照できないシンボルがある。
色々検索してみると、リンクに課題があるようです。
思い切って、`Makefile`を以下にしたら解決しました。

```Makefile:Makefile
obj-m = dpg0100.o
all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
install:
	cp -f dpg0100.ko /lib/modules/$(shell uname -r)/misc
```


### 参考ページ
- [compiling - Help locating linux/version.h - Ask Ubuntu](https://askubuntu.com/questions/390995/help-locating-linux-version-h)
- [Linux-kernel 4.15.0-42-generic （Ubuntu 18.04）環境でのデバイスドライバーのコンパイル - エゼキエル書25章17節……](http://yukarinoki.hatenablog.com/entry/2018/12/09/050802)
- [Linuxカーネルのソースコードを読む方法(その1) - pyopyopyo - Linuxとかプログラミングの覚え書き - ]
(http://pyopyopyo.hatenablog.com/entry/2019/02/19/133956)
- [drivers - asm/system.h header file linux - Super User]
(https://superuser.com/questions/783285/asm-system-h-header-file-linux)
- [error: ‘struct file’ has no member named ‘f_dentry’というコンパイルエラーが出たときは | mipsparcのメモ帳]
(https://mipsparc.wordpress.com/2016/09/16/error-struct-file-has-no-member-named-f_dentry%E3%81%A8%E3%81%84%E3%81%86%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%8C%E5%87%BA/)
- [インターフェイス社ドライバの再コンパイル - Qiita](https://qiita.com/larking95/items/257f99177058d5ed0030)
- [c - Unknown symbol vfs_write (err -2) in kernel module in kernel 4.20 - Stack Overflow](https://stackoverflow.com/questions/53917041/unknown-symbol-vfs-write-err-2-in-kernel-module-in-kernel-4-20/53917521)
 - Unknown symbol ... はカーネルが新しくなって無くなったシンボルであることをここで把握
- [linuxカーネル内部インターフェースの変更例 - Qiita](https://qiita.com/satoru_takeuchi/items/84ce3f86920a327b5128)
- [ローダブル・カーネル・モジュール - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%AD%E3%83%BC%E3%83%80%E3%83%96%E3%83%AB%E3%83%BB%E3%82%AB%E3%83%BC%E3%83%8D%E3%83%AB%E3%83%BB%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB)
- [linux - EXPORT_SYMBOLはシンボルをグローバルにエクスポートしません - 答えた](https://kotaeta.com/54726938)
- [Linux カーネルモジュールのコンパイル - Qiita](https://qiita.com/eggman/items/f5da299037b29478dc41)