diff --git a/README.adoc b/README.adoc index a3f6527..5d50fcb 100644 --- a/README.adoc +++ b/README.adoc @@ -1797,12 +1797,12 @@ since GDB does not know that libc is loaded. This is the userland debug setup most likely to work, since at init time there is only one userland executable running. -For executables from the link:userland/[] directory such as link:userland/count.c[]: +For executables from the link:userland/[] directory such as link:userland/posix/count.c[]: * Shell 1: + .... -./run --wait-gdb --kernel-cli 'init=/lkmc/count.out' +./run --wait-gdb --kernel-cli 'init=/lkmc/posix/count.out' .... * Shell 2: + @@ -3443,10 +3443,10 @@ Determining the right number to put there is of course highly non-trivial and wo We don't have this failure for QEMU, only gem5. QEMU by default copies the host `uname`, but it also has the `-r` option to set it explicitly, try it out with: .... -./run --arch aarch64 --userland uname -- -r v4.17.0 +./run --arch aarch64 --userland ./posix/uname -- -r v4.17.0 .... -Source: link:userland/uname.c[]. +Source: link:userland/posix/uname.c[]. The QEMU source that does this is at: https://github.com/qemu/qemu/blob/v3.1.0/linux-user/syscall.c#L8931 @@ -6418,7 +6418,7 @@ Outcome: the test passes: Sources: * link:kernel_modules/mmap.c[] -* link:userland/mmap.c[] +* link:userland/kernel_modules/mmap.c[] * link:rootfs_overlay/lkmc/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. @@ -6979,7 +6979,7 @@ The program: Then, translate the virtual address to physical using `/proc//maps` and `/proc//pagemap`: .... -./virt_to_phys_user.out 110 0x600800 +./linux/virt_to_phys_user.out 110 0x600800 .... Sample output physical address: @@ -6988,9 +6988,9 @@ Sample output physical address: 0x7c7b800 .... -Source: link:userland/virt_to_phys_user.c[] +Source: link:userland/linux/virt_to_phys_user.c[] -Now we can verify that `virt_to_phys_user.out` gave the correct physical address in the following ways: +Now we can verify that `linux/virt_to_phys_user.out` gave the correct physical address in the following ways: * <> * <> @@ -7004,7 +7004,7 @@ Bibliography: The `xp` <> command reads memory at a given physical address. -First launch `virt_to_phys_user.out` as described at <>. +First launch `linux/virt_to_phys_user.out` as described at <>. On a second terminal, use QEMU to read the physical address: @@ -7027,7 +7027,7 @@ We could not find however to write to memory from the QEMU monitor, boring. `/dev/mem` exposes access to physical addresses, and we use it through the convenient `devmem` BusyBox utility. -First launch `virt_to_phys_user.out` as described at <>. +First launch `linux/virt_to_phys_user.out` as described at <>. Next, read from the physical address: @@ -7078,20 +7078,20 @@ Bibliography: https://stackoverflow.com/questions/11891979/how-to-access-mmaped- Dump the physical address of all pages mapped to a given process using `/proc//maps` and `/proc//pagemap`. -First launch `virt_to_phys_user.out` as described at <>. Suppose that the output was: +First launch `linux/virt_to_phys_user.out` as described at <>. Suppose that the output was: .... # ./virt_to_phys_test.out & vaddr 0x601048 pid 63 -# ./virt_to_phys_user.out 63 0x601048 +# ./linux/virt_to_phys_user.out 63 0x601048 0x1a61048 .... Now obtain the page map for the process: .... -./pagemap_dump.out 63 +./linux/pagemap_dump.out 63 .... Sample output excerpt: @@ -7106,7 +7106,7 @@ vaddr pfn soft-dirty file/shared swapped present library 7ffff78ec000 1fd4 0 1 0 1 /lib/libuClibc-1.0.30.so .... -Source: link:userland/pagemap_dump.c[] +Source: link:userland/linux/pagemap_dump.c[] Adapted from: https://github.com/dwks/pagemap/blob/8a25747bc79d6080c8b94eac80807a4dceeda57a/pagemap2.c @@ -7146,7 +7146,7 @@ Three zeroes is 12 bits which is 4kB, which is the size of a page. + For example, the virtual address `0x601000` has `pfn` of `0x1a61`, which means that its physical address is `0x1a61000` + -This is consistent with what `virt_to_phys_user.out` told us: the virtual address `0x601048` has physical address `0x1a61048`. +This is consistent with what `linux/virt_to_phys_user.out` told us: the virtual address `0x601048` has physical address `0x1a61048`. + `048` corresponds to the three last zeroes, and is the offset within the page. + @@ -9868,16 +9868,6 @@ Sources: * link:bst-vs-heap[] * link:userland/bst_vs_heap.cpp[] -===== OpenMP - -Implemented by GCC itself, so just a toolchain configuration, no external libs, and we enable it by default: - -.... -./openmp.out -.... - -Source: link:userland/openmp.c[] - ===== BLAS Buildroot supports it, which makes everything just trivial: @@ -11316,6 +11306,32 @@ One "downside" of glibc is that it exercises much more kernel functionality on i Programs under link:userland/c/[] are examples of link:https://en.wikipedia.org/wiki/ANSI_C[ANSI C] programming. +=== GCC C extensions + +==== C empty struct + +Example: link:userland/gcc/empty_struct.c[] + +Documentation: https://gcc.gnu.org/onlinedocs/gcc-8.2.0/gcc/Empty-Structures.html#Empty-Structures + +Question: https://stackoverflow.com/questions/24685399/c-empty-struct-what-does-this-mean-do + +==== OpenMP + +GCC implements the <> threading implementation: https://stackoverflow.com/questions/3949901/pthreads-vs-openmp + +Example: link:userland/gcc/openmp.c[] + +The implementation is built into GCC itself. It is enabled at GCC compile time by `BR2_GCC_ENABLE_OPENMP=y` on Buildroot, and at program compile time by `-fopenmp`. + +It seems to be easier to use for compute parallelism and more language agnostic than POSIX threads. + +pthreads are more versatile though and allow for a superset of OpenMP. + +The implementation lives under `libgomp` in the GCC tree, and is documented at: https://gcc.gnu.org/onlinedocs/libgomp/ + +`strace` shows that OpenMP makes `clone()` syscalls in Linux. TODO: does it actually call `pthread_` functions, or does it make syscalls directly? Or in other words, can it work on <>? A quick grep shows many references to pthreads. + [[cpp]] == C++ @@ -11409,7 +11425,7 @@ System-land assembly cheats will be put under: <>. === Userland assembly C standard library -All examples outside of <> link to the C standard library. +All examples except the <> link to the C standard library. This allows using the C standard library for IO, which is very convenient and portable across host OSes. @@ -11423,6 +11439,23 @@ The C standard library infrastructure is implemented in the following files: * link:userland/arch/arm/common_arch.h[] * link:userland/arch/aarch64/common_arch.h[] +==== Freestanding programs + +Unlike most our other assembly examples, which use the C standard library for portability, examples under `freestanding/` directories don't link to the C standard library. + +As a result, those examples cannot do IO portably, and so they make raw system calls and only be run on one given OS, e.g. Linux: <> + +Such executables are called freestanding because they don't execute the glibc initialization code, but rather start directly on our custom hand written assembly. + +In order to GDB step debug those executables, you will want to use `--no-continue`, e.g.: + +.... +./run --arch aarch64 --userland arch/aarch64/freestanding/hello --wait-gdb +./run-gdb --arch aarch64 --no-continue --userland arch/aarch64/freestanding/hello +.... + +You are now left on the very first instruction of our tiny executable! + === Inline assembly Examples under `arch//c/` directories show to how use inline assembly from higher level languages such as C: @@ -11493,19 +11526,6 @@ The following <> programs illustrate how to make system calls: ** link:userland/arch/aarch64/c/freestanding/hello.c[] ** link:userland/arch/aarch64/c/freestanding/hello_clobbers.c[] -Unlike most our other examples, which use the C standard library for portability, examples under `freestanding/` can be only run on Linux. - -Such executables are called freestanding because they don't execute the glibc initialization code, but rather start directly on our custom hand written assembly. - -In order to GDB step debug those executables, you will want to use `--no-continue`, e.g.: - -.... -./run --arch aarch64 --userland arch/aarch64/freestanding/hello --wait-gdb -./run-gdb --arch aarch64 --no-continue --userland arch/aarch64/freestanding/hello -.... - -You are now left on the very first instruction of our tiny executable! - Determining the ARM syscall numbers: * https://reverseengineering.stackexchange.com/questions/16917/arm64-syscalls-table diff --git a/build-userland b/build-userland index ce8d9ea..2652d2b 100755 --- a/build-userland +++ b/build-userland @@ -140,7 +140,6 @@ Default: build all examples that have their package dependencies met, e.g.: has_packages = set(self.env['has_package']) ccflags = [ '-I', self.env['root_dir'], LF, - '-I', self.env['userland_source_dir'], LF, '-O0', LF, '-Wall', LF, '-Werror', LF, @@ -333,11 +332,12 @@ Default: build all examples that have their package dependencies met, e.g.: if error is not None: print(error) return 1 - self.sh.copy_dir_if_update( - srcdir=build_dir, - destdir=self.env['out_rootfs_overlay_lkmc_dir'], - filter_ext=self.env['userland_build_ext'], - ) + if not self.env['in_tree']: + self.sh.copy_dir_if_update( + srcdir=build_dir, + destdir=self.env['out_rootfs_overlay_lkmc_dir'], + filter_ext=self.env['userland_build_ext'], + ) return 0 def clean(self): diff --git a/rootfs_overlay/lkmc/mmap.sh b/rootfs_overlay/lkmc/mmap.sh index d09e366..4647980 100755 --- a/rootfs_overlay/lkmc/mmap.sh +++ b/rootfs_overlay/lkmc/mmap.sh @@ -1,5 +1,5 @@ #!/bin/sh set -e insmod mmap.ko -./mmap.out /proc/lkmc_mmap 2>&1 1>/dev/null +./kernel_modules/mmap.out /proc/lkmc_mmap 2>&1 1>/dev/null rmmod mmap.ko diff --git a/userland/README.adoc b/userland/README.adoc deleted file mode 100644 index 78fdc90..0000000 --- a/userland/README.adoc +++ /dev/null @@ -1 +0,0 @@ -https://github.com/cirosantilli/linux-kernel-module-cheat#ansi-c diff --git a/userland/arch/main.c b/userland/arch/main.c index 528cf4a..ae5354c 100644 --- a/userland/arch/main.c +++ b/userland/arch/main.c @@ -2,10 +2,10 @@ * https://github.com/cirosantilli/linux-kernel-module-cheat#userland-assembly-c-standard-library */ -#include "stdio.h" -#include "stdint.h" +#include +#include -#include "lkmc.h" +#include int asm_main(uint32_t *line); diff --git a/userland/arch/x86_64/freestanding/hello.S b/userland/arch/x86_64/freestanding/hello.S index 5d959e3..8fc63b4 100644 --- a/userland/arch/x86_64/freestanding/hello.S +++ b/userland/arch/x86_64/freestanding/hello.S @@ -7,15 +7,15 @@ _start: asm_main_after_prologue: /* write */ - mov $1, %rax /* stdout */ - mov $1, %rdi /* buffer */ - mov $msg, %rsi /* len */ - mov $len, %rdx /* syscall number */ + mov $1, %rax /* syscall number */ + mov $1, %rdi /* stdout */ + mov $msg, %rsi /* buffer */ + mov $len, %rdx /* len */ syscall /* exit */ - mov $60, %rax /* exit status */ - mov $0, %rdi /* syscall number */ + mov $60, %rax /* syscall number */ + mov $0, %rdi /* exit status */ syscall msg: .ascii "hello\n" diff --git a/userland/c/README.adoc b/userland/c/README.adoc new file mode 100644 index 0000000..021699d --- /dev/null +++ b/userland/c/README.adoc @@ -0,0 +1 @@ +https://github.com/cirosantilli/linux-kernel-module-cheat#c diff --git a/userland/common_userland.h b/userland/common.h similarity index 100% rename from userland/common_userland.h rename to userland/common.h diff --git a/userland/cpp/README.adoc b/userland/cpp/README.adoc index bb60001..dc55289 100644 --- a/userland/cpp/README.adoc +++ b/userland/cpp/README.adoc @@ -1 +1 @@ -https://github.com/cirosantilli/linux-kernel-module-cheat#ansi-cpp +https://github.com/cirosantilli/linux-kernel-module-cheat#cpp diff --git a/userland/ctrl_alt_del.c b/userland/ctrl_alt_del.c index a3f05c9..e35c7c1 100644 --- a/userland/ctrl_alt_del.c +++ b/userland/ctrl_alt_del.c @@ -8,19 +8,19 @@ #include void signal_handler(int sig) { - write(STDOUT_FILENO, "cad\n", 4); - signal(sig, signal_handler); + write(STDOUT_FILENO, "cad\n", 4); + signal(sig, signal_handler); } int main(void) { - int i = 0; - /* Disable the forced reboot, enable sending SIGINT to init. */ - reboot(RB_DISABLE_CAD); + int i = 0; + /* Disable the forced reboot, enable sending SIGINT to init. */ + reboot(RB_DISABLE_CAD); signal(SIGINT, signal_handler); - while (1) { - sleep(1); - printf("%d\n", i); - i++; - } + while (1) { + sleep(1); + printf("%d\n", i); + i++; + } return EXIT_SUCCESS; } diff --git a/userland/gcc/empty_struct.c b/userland/gcc/empty_struct.c index 1cf5bc8..2a9a2b6 100644 --- a/userland/gcc/empty_struct.c +++ b/userland/gcc/empty_struct.c @@ -1,4 +1,4 @@ -/* Empty struct */ +/* https://github.com/cirosantilli/linux-kernel-module-cheat#c-empty-struct */ #include #include diff --git a/userland/gcc/openmp.c b/userland/gcc/openmp.c new file mode 100644 index 0000000..32b920b --- /dev/null +++ b/userland/gcc/openmp.c @@ -0,0 +1,20 @@ +/* https://github.com/cirosantilli/linux-kernel-module-cheat#openmp */ + +#include +#include + +#include + +int main (void) { + int nthreads, tid; +#pragma omp parallel private(nthreads, tid) + { + tid = omp_get_thread_num(); + printf("Hello World from thread = %d\n", tid); + if (tid == 0) { + nthreads = omp_get_num_threads(); + printf("Number of threads = %d\n", nthreads); + } + } + return EXIT_SUCCESS; +} diff --git a/userland/kernel_modules/mmap.c b/userland/kernel_modules/mmap.c new file mode 100644 index 0000000..de3f3ce --- /dev/null +++ b/userland/kernel_modules/mmap.c @@ -0,0 +1,93 @@ +/* https://github.com/cirosantilli/linux-kernel-module-cheat#mmap */ + +#define _XOPEN_SOURCE 700 +#include +#include +#include +#include +#include /* uintmax_t */ +#include +#include +#include /* sysconf */ + +#include /* virt_to_phys_user */ + +enum { BUFFER_SIZE = 4 }; + +int main(int argc, char **argv) { + int fd; + long page_size; + char *address1, *address2; + char buf[BUFFER_SIZE]; + uintptr_t paddr; + + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + return EXIT_FAILURE; + } + page_size = sysconf(_SC_PAGE_SIZE); + printf("open pathname = %s\n", argv[1]); + fd = open(argv[1], O_RDWR | O_SYNC); + if (fd < 0) { + perror("open"); + assert(0); + } + printf("fd = %d\n", fd); + + /* mmap twice for double fun. */ + puts("mmap 1"); + address1 = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (address1 == MAP_FAILED) { + perror("mmap"); + assert(0); + } + puts("mmap 2"); + address2 = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (address2 == MAP_FAILED) { + perror("mmap"); + return EXIT_FAILURE; + } + assert(address1 != address2); + + /* Read and modify memory. */ + puts("access 1"); + assert(!strcmp(address1, "asdf")); + /* vm_fault */ + puts("access 2"); + assert(!strcmp(address2, "asdf")); + /* vm_fault */ + strcpy(address1, "qwer"); + /* Also modified. So both virtual addresses point to the same physical address. */ + assert(!strcmp(address2, "qwer")); + + /* Check that the physical addresses are the same. + * They are, but TODO why virt_to_phys on kernel gives a different value? */ + assert(!virt_to_phys_user(&paddr, getpid(), (uintptr_t)address1)); + printf("paddr1 = 0x%jx\n", (uintmax_t)paddr); + assert(!virt_to_phys_user(&paddr, getpid(), (uintptr_t)address2)); + printf("paddr2 = 0x%jx\n", (uintmax_t)paddr); + + /* Check that modifications made from userland are also visible from the kernel. */ + read(fd, buf, BUFFER_SIZE); + assert(!memcmp(buf, "qwer", BUFFER_SIZE)); + + /* Modify the data from the kernel, and check that the change is visible from userland. */ + write(fd, "zxcv", 4); + assert(!strcmp(address1, "zxcv")); + assert(!strcmp(address2, "zxcv")); + + /* Cleanup. */ + puts("munmap 1"); + if (munmap(address1, page_size)) { + perror("munmap"); + assert(0); + } + puts("munmap 2"); + if (munmap(address2, page_size)) { + perror("munmap"); + assert(0); + } + puts("close"); + close(fd); + return EXIT_SUCCESS; +} diff --git a/userland/linux/pagemap_dump.c b/userland/linux/pagemap_dump.c new file mode 100644 index 0000000..fe29d92 --- /dev/null +++ b/userland/linux/pagemap_dump.c @@ -0,0 +1,116 @@ +/* https://github.com/cirosantilli/linux-kernel-module-cheat#pagemap_dump-out */ + +#define _XOPEN_SOURCE 700 +#include +#include +#include +#include +#include +#include +#include + +#include /* virt_to_phys_user */ + +int main(int argc, char **argv) { + char buffer[BUFSIZ]; + char maps_file[BUFSIZ]; + char pagemap_file[BUFSIZ]; + int maps_fd; + int offset = 0; + int pagemap_fd; + pid_t pid; + + if (argc < 2) { + printf("Usage: %s pid\n", argv[0]); + return EXIT_FAILURE; + } + pid = strtoull(argv[1], NULL, 0); + snprintf(maps_file, sizeof(maps_file), "/proc/%ju/maps", (uintmax_t)pid); + snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)pid); + maps_fd = open(maps_file, O_RDONLY); + if (maps_fd < 0) { + perror("open maps"); + return EXIT_FAILURE; + } + pagemap_fd = open(pagemap_file, O_RDONLY); + if (pagemap_fd < 0) { + perror("open pagemap"); + return EXIT_FAILURE; + } + printf("vaddr pfn soft-dirty file/shared swapped present library\n"); + for (;;) { + ssize_t length = read(maps_fd, buffer + offset, sizeof buffer - offset); + if (length <= 0) break; + length += offset; + for (size_t i = offset; i < (size_t)length; i++) { + uintptr_t low = 0, high = 0; + if (buffer[i] == '\n' && i) { + const char *lib_name; + size_t y; + /* Parse a line from maps. Each line contains a range that contains many pages. */ + { + size_t x = i - 1; + while (x && buffer[x] != '\n') x--; + if (buffer[x] == '\n') x++; + while (buffer[x] != '-' && x < sizeof buffer) { + char c = buffer[x++]; + low *= 16; + if (c >= '0' && c <= '9') { + low += c - '0'; + } else if (c >= 'a' && c <= 'f') { + low += c - 'a' + 10; + } else { + break; + } + } + while (buffer[x] != '-' && x < sizeof buffer) x++; + if (buffer[x] == '-') x++; + while (buffer[x] != ' ' && x < sizeof buffer) { + char c = buffer[x++]; + high *= 16; + if (c >= '0' && c <= '9') { + high += c - '0'; + } else if (c >= 'a' && c <= 'f') { + high += c - 'a' + 10; + } else { + break; + } + } + lib_name = 0; + for (int field = 0; field < 4; field++) { + x++; + while(buffer[x] != ' ' && x < sizeof buffer) x++; + } + while (buffer[x] == ' ' && x < sizeof buffer) x++; + y = x; + while (buffer[y] != '\n' && y < sizeof buffer) y++; + buffer[y] = 0; + lib_name = buffer + x; + } + /* Get info about all pages in this page range with pagemap. */ + { + PagemapEntry entry; + for (uintptr_t vaddr = low; vaddr < high; vaddr += sysconf(_SC_PAGE_SIZE)) { + /* TODO always fails for the last page (vsyscall), why? pread returns 0. */ + if (!pagemap_get_entry(&entry, pagemap_fd, vaddr)) { + printf( + "%jx %jx %u %u %u %u %s\n", + (uintmax_t)vaddr, + (uintmax_t)entry.pfn, + entry.soft_dirty, + entry.file_page, + entry.swapped, + entry.present, + lib_name + ); + } + } + } + buffer[y] = '\n'; + } + } + } + close(maps_fd); + close(pagemap_fd); + return EXIT_SUCCESS; +} diff --git a/userland/linux/virt_to_phys_user.c b/userland/linux/virt_to_phys_user.c new file mode 100644 index 0000000..e35dfc2 --- /dev/null +++ b/userland/linux/virt_to_phys_user.c @@ -0,0 +1,25 @@ +/* https://github.com/cirosantilli/linux-kernel-module-cheat#userland-physical-address-experiments */ + +#define _XOPEN_SOURCE 700 +#include /* printf */ +#include /* EXIT_SUCCESS, EXIT_FAILURE, strtoull */ + +#include /* virt_to_phys_user */ + +int main(int argc, char **argv) { + pid_t pid; + uintptr_t vaddr, paddr = 0; + + if (argc < 3) { + printf("Usage: %s pid vaddr\n", argv[0]); + return EXIT_FAILURE; + } + pid = strtoull(argv[1], NULL, 0); + vaddr = strtoull(argv[2], NULL, 0); + if (virt_to_phys_user(&paddr, pid, vaddr)) { + fprintf(stderr, "error: virt_to_phys_user\n"); + return EXIT_FAILURE; + }; + printf("0x%jx\n", (uintmax_t)paddr); + return EXIT_SUCCESS; +} diff --git a/userland/mmap.c b/userland/mmap.c deleted file mode 100644 index 321c32f..0000000 --- a/userland/mmap.c +++ /dev/null @@ -1,94 +0,0 @@ -/* https://github.com/cirosantilli/linux-kernel-module-cheat#mmap */ - -#define _XOPEN_SOURCE 700 -#include -#include -#include -#include -#include /* uintmax_t */ -#include -#include -#include /* sysconf */ - -#include "common_userland.h" /* virt_to_phys_user */ - -enum { BUFFER_SIZE = 4 }; - -int main(int argc, char **argv) -{ - int fd; - long page_size; - char *address1, *address2; - char buf[BUFFER_SIZE]; - uintptr_t paddr; - - if (argc < 2) { - printf("Usage: %s \n", argv[0]); - return EXIT_FAILURE; - } - page_size = sysconf(_SC_PAGE_SIZE); - printf("open pathname = %s\n", argv[1]); - fd = open(argv[1], O_RDWR | O_SYNC); - if (fd < 0) { - perror("open"); - assert(0); - } - printf("fd = %d\n", fd); - - /* mmap twice for double fun. */ - puts("mmap 1"); - address1 = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (address1 == MAP_FAILED) { - perror("mmap"); - assert(0); - } - puts("mmap 2"); - address2 = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (address2 == MAP_FAILED) { - perror("mmap"); - return EXIT_FAILURE; - } - assert(address1 != address2); - - /* Read and modify memory. */ - puts("access 1"); - assert(!strcmp(address1, "asdf")); - /* vm_fault */ - puts("access 2"); - assert(!strcmp(address2, "asdf")); - /* vm_fault */ - strcpy(address1, "qwer"); - /* Also modified. So both virtual addresses point to the same physical address. */ - assert(!strcmp(address2, "qwer")); - - /* Check that the physical addresses are the same. - * They are, but TODO why virt_to_phys on kernel gives a different value? */ - assert(!virt_to_phys_user(&paddr, getpid(), (uintptr_t)address1)); - printf("paddr1 = 0x%jx\n", (uintmax_t)paddr); - assert(!virt_to_phys_user(&paddr, getpid(), (uintptr_t)address2)); - printf("paddr2 = 0x%jx\n", (uintmax_t)paddr); - - /* Check that modifications made from userland are also visible from the kernel. */ - read(fd, buf, BUFFER_SIZE); - assert(!memcmp(buf, "qwer", BUFFER_SIZE)); - - /* Modify the data from the kernel, and check that the change is visible from userland. */ - write(fd, "zxcv", 4); - assert(!strcmp(address1, "zxcv")); - assert(!strcmp(address2, "zxcv")); - - /* Cleanup. */ - puts("munmap 1"); - if (munmap(address1, page_size)) { - perror("munmap"); - assert(0); - } - puts("munmap 2"); - if (munmap(address2, page_size)) { - perror("munmap"); - assert(0); - } - puts("close"); - close(fd); - return EXIT_SUCCESS; -} diff --git a/userland/openblas_hello.c b/userland/openblas_hello.c index 581bcfb..765fa89 100644 --- a/userland/openblas_hello.c +++ b/userland/openblas_hello.c @@ -1,15 +1,39 @@ /* https://github.com/cirosantilli/linux-kernel-module-cheat#blas - * Adapted from: https://github.com/xianyi/OpenBLAS/wiki/User-Manual/59b62f98e7400270fb03ad1d85fba5b64ebbff2b#call-cblas-interface */ - -#include "lkmc.h" + * Adapted from: https://github.com/xianyi/OpenBLAS/wiki/User-Manual/59b62f98e7400270fb03ad1d85fba5b64ebbff2b#call-cblas-interface + */ #include #include +#include + 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}; - double C[9] = {0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5}; - cblas_dgemm(CblasColMajor, CblasNoTrans, CblasTrans, 3, 3, 2, 1, A, 3, B, 3, 2, C, 3); - assert(lkmc_vector_equal(9, C, (double[]){11.0, -9.0, 5.0, -9.0, 21.0, -1.0, 5.0, -1.0, 3.0}, 1e-6)); + 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 + }; + double C[9] = { + 0.5, 0.5, 0.5, + 0.5, 0.5, 0.5, + 0.5, 0.5, 0.5 + }; + cblas_dgemm( + CblasColMajor, CblasNoTrans, CblasTrans, + 3, 3, 2, 1, A, 3, B, 3, 2, C, 3 + ); + assert(lkmc_vector_equal( + 9, + C, + (double[]) { + 11.0, -9.0, 5.0, + -9.0, 21.0, -1.0, + 5.0, -1.0, 3.0 + }, + 1e-6 + )); } diff --git a/userland/openmp.c b/userland/openmp.c deleted file mode 100644 index 2074aa4..0000000 --- a/userland/openmp.c +++ /dev/null @@ -1,19 +0,0 @@ -/* https://github.com/cirosantilli/linux-kernel-module-cheat#openmp */ - -#include -#include -#include - -int main () { - int nthreads, tid; -#pragma omp parallel private(nthreads, tid) - { - tid = omp_get_thread_num(); - printf("Hello World from thread = %d\n", tid); - if (tid == 0) { - nthreads = omp_get_num_threads(); - printf("Number of threads = %d\n", nthreads); - } - } - return EXIT_SUCCESS; -} diff --git a/userland/pagemap_dump.c b/userland/pagemap_dump.c deleted file mode 100644 index f3553a4..0000000 --- a/userland/pagemap_dump.c +++ /dev/null @@ -1,116 +0,0 @@ -/* https://github.com/cirosantilli/linux-kernel-module-cheat#pagemap_dump-out */ - -#define _XOPEN_SOURCE 700 -#include -#include -#include -#include -#include -#include -#include - -#include "common_userland.h" /* pagemap_get_entry */ - -int main(int argc, char **argv) -{ - char buffer[BUFSIZ]; - char maps_file[BUFSIZ]; - char pagemap_file[BUFSIZ]; - int maps_fd; - int offset = 0; - int pagemap_fd; - pid_t pid; - - if (argc < 2) { - printf("Usage: %s pid\n", argv[0]); - return EXIT_FAILURE; - } - pid = strtoull(argv[1], NULL, 0); - snprintf(maps_file, sizeof(maps_file), "/proc/%ju/maps", (uintmax_t)pid); - snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)pid); - maps_fd = open(maps_file, O_RDONLY); - if (maps_fd < 0) { - perror("open maps"); - return EXIT_FAILURE; - } - pagemap_fd = open(pagemap_file, O_RDONLY); - if (pagemap_fd < 0) { - perror("open pagemap"); - return EXIT_FAILURE; - } - printf("vaddr pfn soft-dirty file/shared swapped present library\n"); - for (;;) { - ssize_t length = read(maps_fd, buffer + offset, sizeof buffer - offset); - if (length <= 0) break; - length += offset; - for (size_t i = offset; i < (size_t)length; i++) { - uintptr_t low = 0, high = 0; - if (buffer[i] == '\n' && i) { - const char *lib_name; - size_t y; - /* Parse a line from maps. Each line contains a range that contains many pages. */ - { - size_t x = i - 1; - while (x && buffer[x] != '\n') x--; - if (buffer[x] == '\n') x++; - while (buffer[x] != '-' && x < sizeof buffer) { - char c = buffer[x++]; - low *= 16; - if (c >= '0' && c <= '9') { - low += c - '0'; - } else if (c >= 'a' && c <= 'f') { - low += c - 'a' + 10; - } else { - break; - } - } - while (buffer[x] != '-' && x < sizeof buffer) x++; - if (buffer[x] == '-') x++; - while (buffer[x] != ' ' && x < sizeof buffer) { - char c = buffer[x++]; - high *= 16; - if (c >= '0' && c <= '9') { - high += c - '0'; - } else if (c >= 'a' && c <= 'f') { - high += c - 'a' + 10; - } else { - break; - } - } - lib_name = 0; - for (int field = 0; field < 4; field++) { - x++; - while(buffer[x] != ' ' && x < sizeof buffer) x++; - } - while (buffer[x] == ' ' && x < sizeof buffer) x++; - y = x; - while (buffer[y] != '\n' && y < sizeof buffer) y++; - buffer[y] = 0; - lib_name = buffer + x; - } - /* Get info about all pages in this page range with pagemap. */ - { - PagemapEntry entry; - for (uintptr_t vaddr = low; vaddr < high; vaddr += sysconf(_SC_PAGE_SIZE)) { - /* TODO always fails for the last page (vsyscall), why? pread returns 0. */ - if (!pagemap_get_entry(&entry, pagemap_fd, vaddr)) { - printf("%jx %jx %u %u %u %u %s\n", - (uintmax_t)vaddr, - (uintmax_t)entry.pfn, - entry.soft_dirty, - entry.file_page, - entry.swapped, - entry.present, - lib_name - ); - } - } - } - buffer[y] = '\n'; - } - } - } - close(maps_fd); - close(pagemap_fd); - return EXIT_SUCCESS; -} diff --git a/userland/count.c b/userland/posix/count.c similarity index 78% rename from userland/count.c rename to userland/posix/count.c index b606f6c..d48e6ab 100644 --- a/userland/count.c +++ b/userland/posix/count.c @@ -1,4 +1,6 @@ -/* Count to infinity with 1 second sleep between each increment. */ +/* Count to infinity with 1 second sleep between each increment. + * Sample application: https://github.com/cirosantilli/linux-kernel-module-cheat#gdb-step-debug-userland-custom-init + */ #define _XOPEN_SOURCE 700 #include diff --git a/userland/virt_to_phys_user.c b/userland/virt_to_phys_user.c deleted file mode 100644 index 72cf1fd..0000000 --- a/userland/virt_to_phys_user.c +++ /dev/null @@ -1,26 +0,0 @@ -/* https://github.com/cirosantilli/linux-kernel-module-cheat#userland-physical-address-experiments */ - -#define _XOPEN_SOURCE 700 -#include /* printf */ -#include /* EXIT_SUCCESS, EXIT_FAILURE, strtoull */ - -#include "common_userland.h" /* virt_to_phys_user */ - -int main(int argc, char **argv) -{ - pid_t pid; - uintptr_t vaddr, paddr = 0; - - if (argc < 3) { - printf("Usage: %s pid vaddr\n", argv[0]); - return EXIT_FAILURE; - } - pid = strtoull(argv[1], NULL, 0); - vaddr = strtoull(argv[2], NULL, 0); - if (virt_to_phys_user(&paddr, pid, vaddr)) { - fprintf(stderr, "error: virt_to_phys_user\n"); - return EXIT_FAILURE; - }; - printf("0x%jx\n", (uintmax_t)paddr); - return EXIT_SUCCESS; -}