diff --git a/README.md b/README.md index a91af0f..6897b1a 100644 --- a/README.md +++ b/README.md @@ -123,20 +123,30 @@ We have not managed to track this problem down yet, but the following workaround This started happening when we switched to building QEMU through Buildroot, and has not been observed on later Ubuntu. +Using text mode is another workaround if you don't need GUI features. + ## Filesystem persistency The root filesystem is persistent across: ./runqemu - ./runqemu + date >f + sync -However, when you do: +then: + + ./runqemu + cat f + +This is particularly useful to re-run shell commands from the history of a previous session with `Ctrl + R`. + +When you do: ./run -it gets overwritten by a fresh filesystem and you lose all changes. +the disk image gets overwritten by a fresh filesystem and you lose all changes. -Using text mode is another workaround if you don't need GUI features. +Remember that if you forcibly turn QEMU off without `sync` or `poweroff` from inside the VM, disk changes may not be saved. ## Text mode diff --git a/kernel_module/user/README.md b/kernel_module/user/README.md index 2e66dc7..a9a41d5 100644 --- a/kernel_module/user/README.md +++ b/kernel_module/user/README.md @@ -12,6 +12,8 @@ These programs can also be compiled and used on host. 1. [myinsmod](myinsmod.c) 1. [myrmmod](myrmmod.c) 1. [init_hello](init_hello.c) + 1. [usermem](usermem.c) + 1. [pagemap_dump](pagemap_dump.c) 1. Module tests 1. [anonymous_inode](anonymous_inode.c) 1. [poll](poll.c) diff --git a/kernel_module/user/pagemap_dump.c b/kernel_module/user/pagemap_dump.c new file mode 100644 index 0000000..724e1e2 --- /dev/null +++ b/kernel_module/user/pagemap_dump.c @@ -0,0 +1,117 @@ +/* +Only tested in x86_64. + +Adapted from: https://github.com/dwks/pagemap/blob/8a25747bc79d6080c8b94eac80807a4dceeda57a/pagemap2.c + +Dump the page map of a given process PID. + +Data sources: /proc/PIC/{map,pagemap} +*/ + +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#define PAGE_SIZE 0x1000 + +int main(int argc, char *argv[]) { + char buffer[BUFSIZ]; + char maps_file[BUFSIZ]; + char pagemap_file[BUFSIZ]; + int maps; + int offset = 0; + int pagemap; + + if (argc < 2) { + printf("Usage: %s pid1\n", argv[0]); + return EXIT_FAILURE; + } + pid_t pid = (pid_t)strtoul(argv[1], NULL, 0); + snprintf(maps_file, sizeof(maps_file), "/proc/%lu/maps", (unsigned long)pid); + snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%lu/pagemap", (unsigned long)pid); + maps = open(maps_file, O_RDONLY); + if (maps < 0) { + perror("open maps"); + return EXIT_FAILURE; + } + pagemap = open(pagemap_file, O_RDONLY); + if (pagemap < 0) { + perror("open pagemap"); + return EXIT_FAILURE; + } + printf("addr pfn soft-dirty file/shared swapped present library\n"); + for (;;) { + ssize_t length = read(maps, buffer + offset, sizeof buffer - offset); + if (length <= 0) break; + length += offset; + for (size_t i = offset; i < (size_t)length; i++) { + unsigned long low = 0, high = 0; + if (buffer[i] == '\n' && i) { + size_t x = i - 1; + while(x && buffer[x] != '\n') x --; + if (buffer[x] == '\n') x++; + size_t beginning = 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; + } + + const char *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++; + size_t y = x; + while (buffer[y] != '\n' && y < sizeof buffer) y++; + buffer[y] = 0; + lib_name = buffer + x; + { + unsigned long data; + for (unsigned long i = low; i < high; i += PAGE_SIZE) { + unsigned long index = (i / PAGE_SIZE) * sizeof(data); + if (pread(pagemap, &data, sizeof(data), index) != sizeof(data)) { + if (errno) perror("pread"); + break; + } + printf("%lx %lx %d %d %d %d %s\n", + i, + data & 0x7fffffffffffff, + (data >> 55) & 1, + (data >> 61) & 1, + (data >> 62) & 1, + (data >> 63) & 1, + lib_name + ); + } + } + } + } + } + close(maps); + close(pagemap); + return 0; +} diff --git a/kernel_module/user/usermem.c b/kernel_module/user/usermem.c new file mode 100644 index 0000000..cb77b52 --- /dev/null +++ b/kernel_module/user/usermem.c @@ -0,0 +1,69 @@ +/* +Only tested in x86_64. + +Provide an allocated userland memory address for us to test out kernel memory APIs, including: + +- /proc/pid/maps +- /proc/pid/pagemap +- /dev/mem + +Usage: + + /usermem.out & + +Outputs address and pid, e.g.: + + address 0x600800 + pid 110 + +Now translate the virtual address to physical for the given PID: + + /pagemap2.out 110 | grep 0x600000 + +where 0x600000 is the page that contains 0x600800. + +This produces a line of type: + + 0x600000 0x7c7b 0 0 0 1 /pagemap_test.out + +where 0x7c7b is the PFN. To get the physical address, just add three zeros back: + + 0x7c7b000 + +Examine the physical memory from the QEMU monitor: on host: + + ./qemumonitor + xp 0x7c7b800 + +Output: + + 0000000007c7b800: 0x12345678 + +Yes!!! We read the correct value from the physical address. + +TODO: why does: + + devmem2 0x7c7b800 + +fail on the mmap? CONFIG_STRICT_DEVMEM is not set. +*/ + +#define _XOPEN_SOURCE 700 +#include +#include +#include +#include + +enum { I0 = 0x12345678 }; + +static volatile uint32_t i = I0; + +int main(void) { + printf("address %p\n", (void *)&i); + printf("pid %ju\n", (uintmax_t)getpid()); + while (i == I0) { + sleep(1); + } + printf("i %jx\n", (uintmax_t)i); + return EXIT_SUCCESS; +}