Build userland examples separately

This commit is contained in:
Ciro Santilli 六四事件 法轮功
2018-10-02 00:00:00 +00:00
parent bc73cebff1
commit 76b486c274
43 changed files with 175 additions and 79 deletions

View File

@@ -1602,7 +1602,7 @@ We can set and get which cores the Linux kernel allows a program to run on with
./run --cpus 2 --eval-busybox '/sched_getaffinity.out'
....
Source: link:packages/kernel_modules/user/sched_getaffinity.c[]
Source: link:packages/kernel_modules/userland/sched_getaffinity.c[]
Sample output:
@@ -2108,7 +2108,7 @@ Sources:
* link:packages/kernel_modules/ring0.c[]
* link:packages/kernel_modules/ring0.h[]
* link:packages/kernel_modules/user/ring0.c[]
* link:packages/kernel_modules/userland/ring0.c[]
In both cases, we attempt to run the exact same code which is shared on the `ring0.h` header file.
@@ -2258,7 +2258,7 @@ but why not just use our minimal `/poweroff.out` and be done with it?
./run --eval '/poweroff.out'
....
Source: link:packages/kernel_modules/user/poweroff.c[]
Source: link:packages/kernel_modules/userland/poweroff.c[]
This also illustrates how to shutdown the computer from C: https://stackoverflow.com/questions/28812514/how-to-shutdown-linux-using-c-or-qt-without-call-to-system
@@ -2270,7 +2270,7 @@ I dare you to guess what this does:
./run --eval '/sleep_forever.out'
....
Source: link:packages/kernel_modules/user/sleep_forever.c[]
Source: link:packages/kernel_modules/userland/sleep_forever.c[]
This executable is a convenient simple init that does not panic and sleeps instead.
@@ -2363,7 +2363,7 @@ TERM=linux
asdf=qwer
....
Source: link:packages/kernel_modules/user/init_env_poweroff.c[].
Source: link:packages/kernel_modules/userland/init_env_poweroff.c[].
==== init environment args
@@ -2790,8 +2790,8 @@ which teaches you how it is done from C code.
Source:
* link:packages/kernel_modules/user/myinsmod.c[]
* link:packages/kernel_modules/user/myrmmod.c[]
* link:packages/kernel_modules/userland/myinsmod.c[]
* link:packages/kernel_modules/userland/myrmmod.c[]
The Linux kernel offers two system calls for module insertion:
@@ -4763,7 +4763,7 @@ Sources:
* link:packages/kernel_modules/ioctl.c[]
* link:packages/kernel_modules/ioctl.h[]
* link:packages/kernel_modules/user/ioctl.c[]
* link:packages/kernel_modules/userland/ioctl.c[]
* link:rootfs_overlay/ioctl.sh[]
`ioctl` is one of the most important methods of communication with real device drivers, which often take several fields as input.
@@ -4811,7 +4811,7 @@ Outcome: the test passes:
Sources:
* link:packages/kernel_modules/mmap.c[]
* link:packages/kernel_modules/user/mmap.c[]
* link:packages/kernel_modules/userland/mmap.c[]
* link:rootfs_overlay/mmap.sh[]
In this example, we make a tiny 4 byte kernel buffer available to user-space, and we then modify it on userspace, and check that the kernel can see the modification.
@@ -4848,7 +4848,7 @@ Sources:
* link:packages/kernel_modules/anonymous_inode.c[]
* link:packages/kernel_modules/anonymous_inode.h[]
* link:packages/kernel_modules/user/anonymous_inode.c[]
* link:packages/kernel_modules/userland/anonymous_inode.c[]
* link:rootfs_overlay/anonymous_inode.sh[]
This example gets an anonymous inode via <<ioctl>> from a debugfs entry by using `anon_inode_getfd`.
@@ -4876,7 +4876,7 @@ Sources:
* link:packages/kernel_modules/netlink.c[]
* link:packages/kernel_modules/netlink.h[]
* link:packages/kernel_modules/user/netlink.c[]
* link:packages/kernel_modules/userland/netlink.c[]
* link:rootfs_overlay/netlink.sh[]
Launch multiple user requests in parallel to stress our socket:
@@ -5354,7 +5354,7 @@ First get a virtual address to play with:
/virt_to_phys_test.out &
....
Source: link:packages/kernel_modules/user/virt_to_phys_test.c[]
Source: link:packages/kernel_modules/userland/virt_to_phys_test.c[]
Sample output:
@@ -5381,7 +5381,7 @@ Sample output physical address:
0x7c7b800
....
Source: link:packages/kernel_modules/user/virt_to_phys_user.c[]
Source: link:packages/kernel_modules/userland/virt_to_phys_user.c[]
Now we can verify that `virt_to_phys_user.out` gave the correct physical address in the following ways:
@@ -5498,7 +5498,7 @@ vaddr pfn soft-dirty file/shared swapped present library
7ffff78ec000 1fd4 0 1 0 1 /lib/libuClibc-1.0.30.so
....
Source: link:packages/kernel_modules/user/pagemap_dump.c[]
Source: link:packages/kernel_modules/userland/pagemap_dump.c[]
Adapted from: https://github.com/dwks/pagemap/blob/8a25747bc79d6080c8b94eac80807a4dceeda57a/pagemap2.c
@@ -5593,7 +5593,7 @@ a
#
....
Source: link:packages/kernel_modules/user/proc_events.c[]
Source: link:packages/kernel_modules/userland/proc_events.c[]
TODO: why `exit: tid=79` shows after `exit: tid=80`?
@@ -6003,7 +6003,7 @@ UIO interface in a nutshell:
Sources:
* link:packages/kernel_modules/user/uio_read.c[]
* link:packages/kernel_modules/userland/uio_read.c[]
* link:rootfs_overlay/uio_read.sh[]
Bibliography:
@@ -6134,7 +6134,7 @@ Minimal example:
./run --kernel-cli 'init=/ctrl_alt_del.out' --graphic
....
Source: link:packages/kernel_modules/user/ctrl_alt_del.c[]
Source: link:packages/kernel_modules/userland/ctrl_alt_del.c[]
When you hit `Ctrl-Alt-Del` in the guest, our tiny init handles a `SIGINT` sent by the kernel and outputs to stdout:
@@ -6444,11 +6444,12 @@ Looks like a recompile is needed to modify the image...
DRM / DRI is the new interface that supersedes `fbdev`:
....
./build-buildroot --buildroot-config 'BR2_PACKAGE_LIBDRM=y' --kernel-modules
./build-buildroot --buildroot-config 'BR2_PACKAGE_LIBDRM=y'
./build-userland --has-package libdrm -- libdrm_modeset
./run --eval-busybox '/libdrm_modeset.out' --graphic
....
Source: link:packages/kernel_modules/user/libdrm_modeset.c[]
Source: link:packages/kernel_modules/userland/libdrm_modeset.c[]
Outcome: for a few seconds, the screen that contains the terminal gets taken over by changing colors of the rainbow.
@@ -8409,7 +8410,7 @@ and then feed `bst_vs_heap.dat` into: https://github.com/cirosantilli/cpp-cheat/
Sources:
* link:bst-vs-heap[]
* link:packages/kernel_modules/user/bst_vs_heap.cpp[]
* link:packages/kernel_modules/userland/bst_vs_heap.cpp[]
===== OpenMP
@@ -8419,15 +8420,16 @@ Implemented by GCC itself, so just a toolchain configuration, no external libs,
/openmp.out
....
Source: link:packages/kernel_modules/user/openmp.c[]
Source: link:packages/kernel_modules/userland/openmp.c[]
===== BLAS
Buildroot supports it, which makes everything just trivial:
....
./build-buildroot --buildroot-config 'BR2_PACKAGE_OPENBLAS=y' --kernel-modules
./run --eval-busybox '/openblas.out; echo $?'
./build-buildroot --buildroot-config 'BR2_PACKAGE_OPENBLAS=y'
./build-userland --has-package openblas -- openblas_hello
./run --eval-busybox '/openblas_hello.out; echo $?'
....
Outcome: the test passes:
@@ -8436,7 +8438,7 @@ Outcome: the test passes:
0
....
Source: link:packages/kernel_modules/user/openblas.c[]
Source: link:packages/kernel_modules/userland/openblas.c[]
The test performs a general matrix multiplication:
@@ -8465,13 +8467,14 @@ cblas_dgemm( CblasColMajor, CblasNoTrans, CblasTrans,3,3,2 ,1, A,3, B,
Header only linear algebra library with a mainline Buildroot package:
....
./build-buildroot --buildroot-config 'BR2_PACKAGE_EIGEN=y' --kernel-modules
./build-buildroot --buildroot-config 'BR2_PACKAGE_EIGEN=y'
./build-userland --has-package eigen -- eigen_hello
....
Just create an array and print it:
....
./run --eval-busybox '/eigen.out'
./run --eval-busybox '/eigen_hello.out'
....
Output:
@@ -8481,7 +8484,7 @@ Output:
2.5 1.5
....
Source: link:packages/kernel_modules/user/eigen.cpp[]
Source: link:packages/kernel_modules/userland/eigen_hello.cpp[]
This example just creates a matrix and prints it out.
@@ -9167,8 +9170,8 @@ The executable `/m5ops.out` illustrates how to hard code with inline assembly th
Sources:
* link:packages/kernel_modules/user/m5ops.h[]
* link:packages/kernel_modules/user/m5ops.c[]
* link:packages/kernel_modules/userland/m5ops.h[]
* link:packages/kernel_modules/userland/m5ops.c[]
That executable is of course a subset of <<m5>> and useless by itself: its goal is only illustrate how to hardcode some <<m5ops>> yourself as one-liners.
@@ -9310,13 +9313,14 @@ system.cpu.dtb.inst_hits
Let's have some fun and try to correlate the gem5 cycle count `system.cpu.numCycles` with the link:https://en.wikipedia.org/wiki/Time_Stamp_Counter[x86 `rdtsc` instruction] that is supposed to do the same thing:
....
./build-buildroot --gem5 --kernel-modules && \
./build-buildroot --gem5 && \
./build-userland -- rdtsc && \
./run --eval '/rdtsc.out;m5 exit;' --gem5 && \
./gem5-stat && \
:;
....
Source: link:packages/kernel_modules/user/rdtsc.c[]
Source: link:packages/kernel_modules/userland/rdtsc.c[]
`rdtsc` outputs a cycle count which we compare with gem5's `gem5-stat`:
@@ -11024,8 +11028,8 @@ hello cpp
Sources:
* link:packages/kernel_modules/user/hello.c[]
* link:packages/kernel_modules/user/hello_cpp.c[]
* link:packages/kernel_modules/userland/hello.c[]
* link:packages/kernel_modules/userland/hello_cpp.c[]
==== rand_check.out
@@ -11035,7 +11039,7 @@ Print out several parameters that normally change randomly from boot to boot:
./run --eval-busybox '/rand_check.out;/poweroff.out'
....
Source: link:packages/kernel_modules/user/rand_check.c[]
Source: link:packages/kernel_modules/userland/rand_check.c[]
This can be used to check the determinism of:

View File

@@ -10,7 +10,9 @@ import time
import common
parser = common.get_argparse()
parser = common.get_argparse(argparse_args={
'description': 'Build our Linux kernel module examples'
})
common.add_build_arguments(parser)
parser.add_argument(
'--host',
@@ -35,9 +37,6 @@ if args.clean:
else:
start_time = time.time()
os.makedirs(build_dir, exist_ok=True)
# Build kernel modules.
#
# I kid you not, out-of-tree build is not possible, O= does not work as for the kernel build:
#
# * https://stackoverflow.com/questions/5718899/building-an-out-of-tree-linux-kernel-module-in-a-separate-object-directory
@@ -99,24 +98,5 @@ else:
),
cwd=common.kernel_modules_src_dir,
) == 0
# Build userland tools.
if args.host:
allowed_toolchains = ['host']
else:
allowed_toolchains = ['buildroot']
cc = common.get_toolchain_tool('gcc', allowed_toolchains=allowed_toolchains)
cxx = common.get_toolchain_tool('g++', allowed_toolchains=allowed_toolchains)
assert common.run_cmd(
[
'make',
'-j', str(multiprocessing.cpu_count()),
'CC={}'.format(cc),
'CXX={}'.format(cxx),
'OUT_DIR={}'.format(os.path.join(build_dir, 'user')),
],
cwd=os.path.join(common.kernel_modules_src_dir, 'user'),
extra_paths=[common.ccache_dir],
) == 0
end_time = time.time()
common.print_time(end_time - start_time)

65
build-userland Executable file
View File

@@ -0,0 +1,65 @@
#!/usr/bin/env python3
import distutils.file_util
import multiprocessing
import os
import platform
import shutil
import subprocess
import time
import common
parser = common.get_argparse(argparse_args={
'description': 'Build our compiled userland examples',
})
common.add_build_arguments(parser)
parser.add_argument(
'--has-package',
action='append',
default=[],
help='''\
Indicate that a given package is present in the root filesystem, which
allows us to build examples that rely on it.
''',
)
parser.add_argument(
'targets',
default=[],
help='''\
Build only the given userland programs.
Default: build all examples that have their package dependencies met.
For example, an OpenBLAS example can only be built if the target root filesystem
has the OpenBLAS libraries and headers installed.
''',
metavar='programs',
nargs='*',
)
args = common.setup(parser)
if args.clean:
common.rmrf(common.userland_build_dir)
else:
start_time = time.time()
os.makedirs(common.userland_build_dir, exist_ok=True)
allowed_toolchains = ['buildroot']
cc = common.get_toolchain_tool('gcc', allowed_toolchains=allowed_toolchains)
cxx = common.get_toolchain_tool('g++', allowed_toolchains=allowed_toolchains)
assert common.run_cmd(
(
[
'make',
'-j', str(multiprocessing.cpu_count()),
'CC={}'.format(cc),
'CXX={}'.format(cxx),
'PKG_CONFIG={}'.format(common.buildroot_pkg_config),
'STAGING_DIR={}'.format(common.buildroot_staging_dir),
'OUT_DIR={}'.format(common.userland_build_dir),
] +
['HAS_{}=y'.format(package.upper()) for package in args.has_package] +
[os.path.join(common.userland_build_dir, os.path.splitext(os.path.split(target)[1])[0]) + common.executable_ext for target in args.targets]
),
cwd=common.userland_src_dir,
extra_paths=[common.ccache_dir],
) == 0
end_time = time.time()
common.print_time(end_time - start_time)

View File

@@ -28,6 +28,7 @@ out_dir = os.path.join(root_dir, 'out')
bench_boot = os.path.join(out_dir, 'bench-boot.txt')
packages_dir = os.path.join(root_dir, 'packages')
kernel_modules_src_dir = os.path.join(this.packages_dir, 'kernel_modules')
userland_src_dir = os.path.join(this.kernel_modules_src_dir, 'userland')
submodules_dir = os.path.join(root_dir, 'submodules')
buildroot_src_dir = os.path.join(submodules_dir, 'buildroot')
crosstool_ng_src_dir = os.path.join(submodules_dir, 'crosstool-ng')
@@ -53,6 +54,7 @@ asm_ext = '.S'
c_ext = '.c'
kernel_module_ext = '.ko'
obj_ext = '.o'
executable_ext = '.out'
config_file = os.path.join(data_dir, 'config')
if os.path.exists(config_file):
config = imp.load_source('config', config_file)
@@ -548,10 +550,12 @@ def setup(parser):
this.qemu_guest_build_dir = os.path.join(this.build_dir, 'qemu-custom')
this.host_dir = os.path.join(this.buildroot_build_dir, 'host')
this.host_bin_dir = os.path.join(this.host_dir, 'usr', 'bin')
this.buildroot_pkg_config = os.path.join(this.host_bin_dir, 'pkg-config')
this.buildroot_images_dir = os.path.join(this.buildroot_build_dir, 'images')
this.rootfs_raw_file = os.path.join(this.buildroot_images_dir, 'rootfs.ext2')
this.qcow2_file = this.rootfs_raw_file + '.qcow2'
this.staging_dir = os.path.join(this.buildroot_build_dir, 'staging')
this.staging_dir = os.path.join(this.out_dir, 'staging', args.arch)
this.buildroot_staging_dir = os.path.join(this.buildroot_build_dir, 'staging')
this.target_dir = os.path.join(this.buildroot_build_dir, 'target')
this.run_dir_base = os.path.join(this.out_dir, 'run')
this.gem5_run_dir = os.path.join(this.run_dir_base, 'gem5', args.arch, str(args.run_id))
@@ -627,6 +631,7 @@ def setup(parser):
this.kernel_modules_build_base_dir = os.path.join(this.out_dir, 'kernel_modules')
this.kernel_modules_build_dir = os.path.join(this.kernel_modules_build_base_dir, args.arch)
this.kernel_modules_build_host_dir = os.path.join(this.kernel_modules_build_base_dir, 'host')
this.userland_build_dir = os.path.join(this.out_dir, 'userland', args.arch)
# Ports
if args.port_offset is None:

View File

@@ -1,4 +1,13 @@
ifeq ($(OBJECT_FILES),)
obj-m += $(addsuffix .o, $(notdir $(basename $(filter-out %.mod.c, $(wildcard $(BR2_EXTERNAL_KERNEL_MODULES_PATH)/*.c)))))
else
# Trying to do:
# $(MAKE) -C '$(LINUX_DIR)' M='$(M)' hello.ko hello2.ko
# to restrict which modules are built leads to failures
# when doing parallel builds. The only solution I could find
# was to let the host select obj-m itself.
obj-m += $(OBJECT_FILES)
endif
ccflags-y := -DDEBUG -g -std=gnu99 -Werror -Wno-declaration-after-statement -Wframe-larger-than=1000000000
.PHONY: all

View File

@@ -19,10 +19,10 @@ ifeq ($(BR2_PACKAGE_OPENBLAS),y)
endif
define KERNEL_MODULES_BUILD_CMDS
$(MAKE) -C '$(@D)/user' $(TARGET_CONFIGURE_OPTS) \
BR2_PACKAGE_EIGEN="$(BR2_PACKAGE_EIGEN)" \
BR2_PACKAGE_LIBDRM="$(BR2_PACKAGE_LIBDRM)" \
BR2_PACKAGE_OPENBLAS="$(BR2_PACKAGE_OPENBLAS)" \
$(MAKE) -C '$(@D)/userland' $(TARGET_CONFIGURE_OPTS) \
HAS_EIGEN="$(BR2_PACKAGE_EIGEN)" \
HAS_LIBDRM="$(BR2_PACKAGE_LIBDRM)" \
HAS_OPENBLAS="$(BR2_PACKAGE_OPENBLAS)" \
;
endef
@@ -33,7 +33,7 @@ define KERNEL_MODULES_INSTALL_TARGET_CMDS
#
# Modules can be still be easily inserted with "modprobe module" however.
$(INSTALL) -D -m 0655 $(@D)/*.ko '$(TARGET_DIR)'
$(INSTALL) -D -m 0755 $(@D)/user/*.out '$(TARGET_DIR)'
$(INSTALL) -D -m 0755 $(@D)/userland/*.out '$(TARGET_DIR)'
endef
$(eval $(kernel-module))

View File

@@ -1,3 +0,0 @@
https://github.com/cirosantilli/linux-kernel-module-cheat#rootfs_overlay
. link:sched_getaffinity.c[]

View File

@@ -10,7 +10,7 @@ OUT_EXT = .out
OUT_DIR = .
OUTS := $(foreach IN_EXT,$(IN_EXT_C) $(IN_EXT_CXX),$(addsuffix $(OUT_EXT), $(basename $(wildcard *$(IN_EXT)))))
ifeq ($(BR2_PACKAGE_EIGEN),y)
ifeq ($(HAS_EIGEN),y)
CXXFLAGS_EXTRA += -I$(STAGING_DIR)/usr/include/eigen3
# TODO: was failing with:
# fatal error: Eigen/Dense: No such file or directory as of
@@ -20,26 +20,27 @@ ifeq ($(BR2_PACKAGE_EIGEN),y)
else
OUTS := $(filter-out eigen_%$(OUT_EXT),$(OUTS))
endif
ifeq ($(BR2_PACKAGE_LIBDRM),y)
ifeq ($(HAS_LIBDRM),y)
LIBS += $(shell $(PKG_CONFIG) --libs libdrm)
CFLAGS_EXTRA += $(shell $(PKG_CONFIG) --cflags libdrm)
else
OUTS := $(filter-out libdrm_%$(OUT_EXT),$(OUTS))
endif
ifeq ($(BR2_PACKAGE_OPENBLAS),y)
LIBS += -lopenblas
ifeq ($(HAS_OPENBLAS),y)
LIBS += $(shell $(PKG_CONFIG) --libs openblas)
CFLAGS_EXTRA += $(shell $(PKG_CONFIG) --cflags openblas)
else
OUTS := $(filter-out openblas$(OUT_EXT),$(OUTS))
OUTS := $(filter-out openblas_%$(OUT_EXT),$(OUTS))
endif
OUTS := $(addprefix $(OUT_DIR)/,$(OUTS))
all: mkdir $(OUTS)
$(OUT_DIR)/%$(OUT_EXT): %$(IN_EXT_C)
$(CC) $(CFLAGS) $(CFLAGS_EXTRA) -o '$@' '$<' $(LIBS)
$(CC) $(CFLAGS) $(CCFLAGS) $(CFLAGS_EXTRA) -o '$@' '$<' $(LIBS)
$(OUT_DIR)/%$(OUT_EXT): %$(IN_EXT_CXX)
$(CXX) $(CXXFLAGS) $(CXXFLAGS_EXTRA) -o '$@' '$<' $(LIBS)
$(CXX) $(CXXFLAGS) $(CCFLAGS) $(CXXFLAGS_EXTRA) -o '$@' '$<' $(LIBS)
clean:
rm -f *'$(OUT_EXT)'

View File

@@ -0,0 +1 @@
name: USERLAND

View File

@@ -0,0 +1,34 @@
################################################################################
#
# kernel_modules
#
################################################################################
KERNEL_MODULES_VERSION = 1.0
KERNEL_MODULES_SITE = $(BR2_EXTERNAL_KERNEL_MODULES_PATH)
KERNEL_MODULES_SITE_METHOD = local
ifeq ($(BR2_PACKAGE_EIGEN),y)
KERNEL_MODULES_DEPENDENCIES += eigen
endif
ifeq ($(BR2_PACKAGE_LIBDRM),y)
KERNEL_MODULES_DEPENDENCIES += libdrm
endif
ifeq ($(BR2_PACKAGE_OPENBLAS),y)
KERNEL_MODULES_DEPENDENCIES += openblas
endif
define KERNEL_MODULES_BUILD_CMDS
$(MAKE) -C '$(@D)' $(TARGET_CONFIGURE_OPTS) \
BR2_PACKAGE_EIGEN="$(BR2_PACKAGE_EIGEN)" \
BR2_PACKAGE_LIBDRM="$(BR2_PACKAGE_LIBDRM)" \
BR2_PACKAGE_OPENBLAS="$(BR2_PACKAGE_OPENBLAS)" \
;
endef
define KERNEL_MODULES_INSTALL_TARGET_CMDS
$(INSTALL) -D -m 0755 $(@D)/*.out '$(TARGET_DIR)'
endef
$(eval $(kernel-module))
$(eval $(generic-package))

View File

@@ -20,7 +20,7 @@ struct msghdr msg;
struct nlmsghdr *nlh;
struct sockaddr_nl src_addr, dest_addr;
int main()
int main(void)
{
sock_fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_USER);
if (sock_fd < 0) {

View File

@@ -6,7 +6,7 @@
#include <assert.h>
#include <cblas.h>
int main()
int main(void)
{
double A[6] = {1.0, 2.0, 1.0, -3.0, 4.0, -1.0};
double B[6] = {1.0, 2.0, 1.0, -3.0, 4.0, -1.0};

View File

@@ -5,7 +5,7 @@
#if defined(__aarch64__)
int main() {}
int main(void) {}
#else
@@ -147,7 +147,7 @@ static void on_sigint(__attribute__ ((unused)) int unused)
need_exit = true;
}
int main()
int main(void)
{
int nl_sock;
int rc = EXIT_SUCCESS;

View File

@@ -66,7 +66,7 @@ int main(int argc, char **argv)
#include <unistd.h>
#include <unistd.h>
int main()
int main(void)
{
int uiofd;
int configfd;

View File

@@ -18,7 +18,7 @@ args = common.setup(parser)
sys.exit(subprocess.Popen([
common.get_toolchain_tool('gdb'),
'-q',
'-ex', 'set sysroot {}'.format(common.staging_dir),
'-ex', 'set sysroot {}'.format(common.buildroot_staging_dir),
'-ex', 'target remote localhost:{}'.format(common.qemu_hostfwd_generic_port),
'-ex', 'tbreak main',
'-ex', 'continue',