龙芯MIPS64 QEMU ioctl的问题

龙芯 QEMU 问题

最近搞了一块使用国产龙芯 3A3000 处理器的开源开发板,顺便捣鼓移植我们的服务程序,安装龙芯开源社区的 Loongnix 系统之后测试下来 3A3000 处理器的性能还是基本能用的。龙芯处理器自带的内存控制器也能到 DDR3-1600 了,只是目前还在使用 HT3.0 总线确实拖累系统性能。

我们部署的一些服务目前还依赖几个 x86 系统的闭源程序,无法直接在龙芯 MIPS64 系统下运行,就想到使用 QEMU 的 User-mode emulation 来实现了。

Loongnix 系统中的 qemu-i386 运行比较简单的 x86 程序一般都没有问题,不过我发现即使用打了 ioctl patch 的 QEMU 运行其中一个使用了私有 ioctl 调用的 x86 程序仍然会出错,通过开启 qemu-i386 的 strace 可以看到错误信息:

zzm@loongnix:~$ qemu-i386 -strace /lib/i386-linux-gnu/ld-linux.so.2 --library-path /lib/i386-linux-gnu /usr/sbin/test-agent

2459 ioctl(5,-1055634175,-42368,0,0,-42056) = -1 errno=25 (Inappropriate ioctl for device)
2459 ioctl(5,-1055634175,-41488,0,0,-41176) = -1 errno=25 (Inappropriate ioctl for device)
2459 ioctl(5,-1055634175,-44192,0,0,-43880) = -1 errno=25 (Inappropriate ioctl for device)

上面的 /usr/sbin/test-agent 就是要模拟运行的 x86 二进制程序,/lib/i386-linux-gnu 是我在龙芯 MIPS64 系统上增加的 x86 程序依赖基础库目录,其中最重要的就是 libc.so.6ld-linux.so.2 库了。

从 strace 数据应该可以发现该 ioctl 已经发到设备,但是 kernel 返回 Inappropriate ioctl 错误,而 -1055634175 则表示 x86 二进制程序发送的是 compat ioctl,此时我才想起来确认下 qemu-i386 程序是否正确:

zzm@loongnix:~$ file `which qemu-i386`
/usr/bin/qemu-i386: ELF 64-bit LSB executable, MIPS, MIPS64 rel2 version 1 (SYSV), dynamically linked (uses shared libs), BuildID[sha1]=39cac8103f052b4ffa161f615159c888024ef4e3, for GNU/Linux 2.6.32, stripped

龙芯 Loongnix MIPS64 系统目前兼容几种 ABI:

  • O32:MIPS 32 位处理器使用;
  • N64:只适用于 MIPS 64 位处理器,使用 64 位指针和 long integers;
  • N32:只适用于 MIPS 64 位处理器,在保留 MIPS N64 ABI 的几乎所有特性的情况下,使用 32 位指针和 long integers。

这个 qemu-i386 使用的是 MIPS64 N64 ABI,而 qemu-i386 实际运行的 x86 二进制程序在龙芯 64 位 kernel 下会发送 compat ioctl,这样最终 qemu-i386 程序调用 compat ioctl 时就显然会有问题,报 Inappropriate ioctl 也就不奇怪了。

解决方法也就很简单了,我只要重新编译出使用 N32 或者 O32 ABI 的 qemu-i386 程序应该就能解决,为了性能考虑可以尽量使用 N32 ABI。

编译 N32 ABI QEMU

首先需要在 Loongnix MIPS64 系统下安装编译 QEMU 需要的 N32 版本的开发包,这里我只安装了 QEMU 用户模式需要的开发包:

zzm@loongnix:~$ yum install glibc-devel.mipsn32el glib2-devel.mipsn32el zlib-devel.mipsn32el libffi.mipsn32el pixman-devel.mipsn32el

提示

Loongnix 系统的 N64 相关库文件一般放在 /usr/lib64 目录下,N32 相关库文件在 /usr/lib32 目录下,而 O32 相关库文件则在 /usr/lib 目录下。

然后进入 QEMU 源代码目录重新 configure,增加特定编译参数指定生成 N32 位的程序:

zzm@loongnix:qemu$ PKG_CONFIG_LIBDIR=/usr/lib32/pkgconfig ./configure --target-list=i386-linux-user --disable-kvm --disable-xen --enable-pie --extra-cflags=-mabi=n32

简单说明一下:

  • PKG_CONFIG_LIBDIR 环境变量指定使用什么版本的 pkgconfig 查找库及开发包,这里使用 /usr/lib32/pkgconfig N32 版本;
  • --target-list 指定 QEMU 编译目标,这里我们只需要 qemu-i386,因此把 KVM、Xen 等功能也禁用了;
  • --extra-cflags 指定额外编译参数为 -mabi=n32,这样编译和链接时都是 N32 版本了。

重新编译安装之后可以看到新的 qemu-i386 使用的就是 N32 ABI 了:

zzm@loongnix:~$ file `which qemu-i386`
/usr/local/bin/qemu-i386: ELF 32-bit LSB executable, MIPS, N32 MIPS64 rel2 version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=12e0025c42a9a63b8045c31159950f9b185f8745, stripped

最后测试使用新的 qemu-i386 运行依赖 ioctl 的 x86 二进制程序,现在就没有直接报错的问题了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*