pagemap.h 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. #ifndef LKMC_PAGEMAP_H
  2. #define LKMC_PAGEMAP_H
  3. #define _XOPEN_SOURCE 700
  4. #include <fcntl.h> /* open */
  5. #include <stdint.h> /* uint64_t */
  6. #include <stdio.h> /* snprintf */
  7. #include <sys/types.h>
  8. #include <unistd.h> /* pread, sysconf */
  9. /* Format documented at:
  10. * https://github.com/torvalds/linux/blob/v4.9/Documentation/vm/pagemap.txt
  11. */
  12. typedef struct {
  13. uint64_t pfn : 54;
  14. unsigned int soft_dirty : 1;
  15. unsigned int file_page : 1;
  16. unsigned int swapped : 1;
  17. unsigned int present : 1;
  18. } LkmcPagemapEntry;
  19. /* Parse the pagemap entry for the given virtual address.
  20. *
  21. * @param[out] entry the parsed entry
  22. * @param[in] pagemap_fd file descriptor to an open /proc/pid/pagemap file
  23. * @param[in] vaddr virtual address to get entry for
  24. * @return 0 for success, 1 for failure
  25. */
  26. int lkmc_pagemap_get_entry(LkmcPagemapEntry *entry, int pagemap_fd, uintptr_t vaddr) {
  27. size_t nread;
  28. ssize_t ret;
  29. uint64_t data;
  30. uintptr_t vpn;
  31. vpn = vaddr / sysconf(_SC_PAGE_SIZE);
  32. nread = 0;
  33. while (nread < sizeof(data)) {
  34. ret = pread(
  35. pagemap_fd,
  36. &data,
  37. sizeof(data) - nread,
  38. vpn * sizeof(data) + nread
  39. );
  40. nread += ret;
  41. if (ret <= 0) {
  42. return 1;
  43. }
  44. }
  45. entry->pfn = data & (((uint64_t)1 << 54) - 1);
  46. entry->soft_dirty = (data >> 54) & 1;
  47. entry->file_page = (data >> 61) & 1;
  48. entry->swapped = (data >> 62) & 1;
  49. entry->present = (data >> 63) & 1;
  50. return 0;
  51. }
  52. /* Convert the given virtual address to physical using /proc/PID/pagemap.
  53. *
  54. * @param[out] paddr physical address
  55. * @param[in] pid process to convert for
  56. * @param[in] vaddr virtual address to get entry for
  57. * @return 0 for success, 1 for failure
  58. */
  59. int lkmc_pagemap_virt_to_phys_user(uintptr_t *paddr, pid_t pid, uintptr_t vaddr) {
  60. char pagemap_file[BUFSIZ];
  61. int pagemap_fd;
  62. snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)pid);
  63. pagemap_fd = open(pagemap_file, O_RDONLY);
  64. if (pagemap_fd < 0) {
  65. return 1;
  66. }
  67. LkmcPagemapEntry entry;
  68. if (lkmc_pagemap_get_entry(&entry, pagemap_fd, vaddr)) {
  69. return 1;
  70. }
  71. close(pagemap_fd);
  72. *paddr = (entry.pfn * sysconf(_SC_PAGE_SIZE)) + (vaddr % sysconf(_SC_PAGE_SIZE));
  73. return 0;
  74. }
  75. #endif