start the big userland migration

This commit is contained in:
Ciro Santilli 六四事件 法轮功
2019-05-05 00:00:00 +00:00
parent 146e568db8
commit ecef42be81
21 changed files with 377 additions and 332 deletions

View File

@@ -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/<pid>/maps` and `/proc/<pid>/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:
* <<qemu-xp>>
* <<dev-mem>>
@@ -7004,7 +7004,7 @@ Bibliography:
The `xp` <<qemu-monitor>> command reads memory at a given physical address.
First launch `virt_to_phys_user.out` as described at <<userland-physical-address-experiments>>.
First launch `linux/virt_to_phys_user.out` as described at <<userland-physical-address-experiments>>.
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 <<userland-physical-address-experiments>>.
First launch `linux/virt_to_phys_user.out` as described at <<userland-physical-address-experiments>>.
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/<pid>/maps` and `/proc/<pid>/pagemap`.
First launch `virt_to_phys_user.out` as described at <<userland-physical-address-experiments>>. Suppose that the output was:
First launch `linux/virt_to_phys_user.out` as described at <<userland-physical-address-experiments>>. 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 <<OpenMP>> 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 <<freestanding-programs>>? A quick grep shows many references to pthreads.
[[cpp]]
== C++
@@ -11409,7 +11425,7 @@ System-land assembly cheats will be put under: <<baremetal-setup>>.
=== Userland assembly C standard library
All examples outside of <<linux-system-calls,freestanding directories>> link to the C standard library.
All examples except the <<freestanding-programs>> 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: <<linux-system-calls>>
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/<arch>/c/` directories show to how use inline assembly from higher level languages such as C:
@@ -11493,19 +11526,6 @@ The following <<userland-setup>> 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

View File

@@ -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):

View File

@@ -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

View File

@@ -1 +0,0 @@
https://github.com/cirosantilli/linux-kernel-module-cheat#ansi-c

View File

@@ -2,10 +2,10 @@
* https://github.com/cirosantilli/linux-kernel-module-cheat#userland-assembly-c-standard-library
*/
#include "stdio.h"
#include "stdint.h"
#include <stdio.h>
#include <stdint.h>
#include "lkmc.h"
#include <lkmc.h>
int asm_main(uint32_t *line);

View File

@@ -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"

1
userland/c/README.adoc Normal file
View File

@@ -0,0 +1 @@
https://github.com/cirosantilli/linux-kernel-module-cheat#c

View File

@@ -1 +1 @@
https://github.com/cirosantilli/linux-kernel-module-cheat#ansi-cpp
https://github.com/cirosantilli/linux-kernel-module-cheat#cpp

View File

@@ -8,19 +8,19 @@
#include <unistd.h>
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;
}

View File

@@ -1,4 +1,4 @@
/* Empty struct */
/* https://github.com/cirosantilli/linux-kernel-module-cheat#c-empty-struct */
#include <assert.h>
#include <stdlib.h>

20
userland/gcc/openmp.c Normal file
View File

@@ -0,0 +1,20 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#openmp */
#include <stdio.h>
#include <stdlib.h>
#include <omp.h>
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;
}

View File

@@ -0,0 +1,93 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#mmap */
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h> /* uintmax_t */
#include <string.h>
#include <sys/mman.h>
#include <unistd.h> /* sysconf */
#include <userland/common.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 <mmap_file>\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;
}

View File

@@ -0,0 +1,116 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#pagemap_dump-out */
#define _XOPEN_SOURCE 700
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <userland/common.h> /* 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;
}

View File

@@ -0,0 +1,25 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#userland-physical-address-experiments */
#define _XOPEN_SOURCE 700
#include <stdio.h> /* printf */
#include <stdlib.h> /* EXIT_SUCCESS, EXIT_FAILURE, strtoull */
#include <userland/common.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;
}

View File

@@ -1,94 +0,0 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#mmap */
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h> /* uintmax_t */
#include <string.h>
#include <sys/mman.h>
#include <unistd.h> /* 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 <mmap_file>\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;
}

View File

@@ -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 <assert.h>
#include <cblas.h>
#include <lkmc.h>
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
));
}

View File

@@ -1,19 +0,0 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#openmp */
#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
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;
}

View File

@@ -1,116 +0,0 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#pagemap_dump-out */
#define _XOPEN_SOURCE 700
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#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;
}

View File

@@ -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 <limits.h>

View File

@@ -1,26 +0,0 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#userland-physical-address-experiments */
#define _XOPEN_SOURCE 700
#include <stdio.h> /* printf */
#include <stdlib.h> /* 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;
}