Patrick Georgi | ac95903 | 2020-05-05 22:49:26 +0200 | [diff] [blame] | 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
Patrick Rudolph | b1ef725 | 2019-09-28 17:44:01 +0200 | [diff] [blame] | 2 | |
| 3 | #include <stdio.h> |
| 4 | #include <stdlib.h> |
| 5 | #include <unistd.h> |
| 6 | #include <string.h> |
| 7 | #include <sys/types.h> |
| 8 | #include <sys/stat.h> |
| 9 | #include <fcntl.h> |
| 10 | #include <errno.h> |
| 11 | #include <inttypes.h> |
Patrick Georgi | 36c2ea4 | 2020-11-25 16:24:05 +0000 | [diff] [blame] | 12 | #include <getopt.h> |
Patrick Rudolph | b1ef725 | 2019-09-28 17:44:01 +0200 | [diff] [blame] | 13 | |
| 14 | static void usage(char *argv[]) |
| 15 | { |
| 16 | printf("usage: %s -b <addr> -a <arch> -o <file>\n", argv[0]); |
Patrick Georgi | 220c209 | 2020-01-30 12:58:08 +0100 | [diff] [blame] | 17 | printf(" -a\t architecture. Supported: x86_64\n"); |
Patrick Rudolph | b1ef725 | 2019-09-28 17:44:01 +0200 | [diff] [blame] | 18 | printf(" -b\t base address\n"); |
Patrick Rudolph | bc2204e | 2019-11-21 17:33:58 +0100 | [diff] [blame] | 19 | printf(" -o\t the file to write to\n"); |
Patrick Rudolph | b1ef725 | 2019-09-28 17:44:01 +0200 | [diff] [blame] | 20 | printf(" -h\t show this help text\n"); |
| 21 | } |
| 22 | |
| 23 | /* |
| 24 | * For reference see "AMD64 ArchitectureProgrammer's Manual Volume 2", |
| 25 | * Document 24593-Rev. 3.31-July 2019 Chapter 5.3.4 |
| 26 | * |
Patrick Rudolph | 7d28f20 | 2020-06-30 20:17:26 +0200 | [diff] [blame] | 27 | * Page table attributes: WB, User+Supervisor, Present, Writeable, Accessed, Dirty |
Patrick Rudolph | b1ef725 | 2019-09-28 17:44:01 +0200 | [diff] [blame] | 28 | */ |
Patrick Rudolph | e69798b | 2019-11-12 16:56:43 +0100 | [diff] [blame] | 29 | #define _PRES (1ULL << 0) |
| 30 | #define _RW (1ULL << 1) |
| 31 | #define _US (1ULL << 2) |
| 32 | #define _A (1ULL << 5) |
| 33 | #define _D (1ULL << 6) |
| 34 | #define _PS (1ULL << 7) |
| 35 | #define _GEN_DIR(a) (_PRES | _RW | _US | _A | (a)) |
| 36 | #define _GEN_PAGE(a) (_PRES | _RW | _US | _PS | _A | _D | (a)) |
Patrick Rudolph | b1ef725 | 2019-09-28 17:44:01 +0200 | [diff] [blame] | 37 | |
| 38 | /* |
| 39 | * Generate x86_64 page tables. |
| 40 | * The page tables needs to be placed at @base_address, and identity map |
| 41 | * the first @size_gib GiB of physical memory. |
| 42 | */ |
| 43 | static int gen_pgtbl_x86_64(const uint64_t base_address, |
| 44 | const size_t size_gib, |
| 45 | void **out_buf, |
| 46 | size_t *out_size) |
| 47 | { |
| 48 | uint64_t *entry; |
| 49 | |
| 50 | if (!out_size || !out_buf) |
| 51 | return 1; |
| 52 | |
| 53 | *out_size = (size_gib + 2) * 4096; |
| 54 | *out_buf = malloc(*out_size); |
| 55 | if (!*out_buf) |
| 56 | return 1; |
| 57 | |
| 58 | memset(*out_buf, 0, *out_size); |
| 59 | entry = (uint64_t *)*out_buf; |
| 60 | |
| 61 | /* Generate one PM4LE entry - point to PDPE */ |
| 62 | entry[0] = _GEN_DIR(base_address + 4096); |
| 63 | entry += 512; |
| 64 | |
| 65 | /* PDPE table - point to PDE */ |
| 66 | for (size_t i = 0; i < size_gib; i++) |
| 67 | entry[i] = _GEN_DIR(base_address + 4096 * (i + 2)); |
| 68 | entry += 512; |
| 69 | |
| 70 | /* PDE tables - identity map 2MiB pages */ |
| 71 | for (size_t g = 0; g < size_gib; g++) { |
| 72 | for (size_t i = 0; i < 512; i++) { |
| 73 | uint64_t addr = ((1ULL << (12 + 9)) * i) | ((1ULL << (12 + 9 + 9)) * g); |
| 74 | entry[i] = _GEN_PAGE(addr); |
| 75 | } |
| 76 | entry += 512; |
| 77 | } |
| 78 | |
| 79 | return 0; |
| 80 | } |
| 81 | |
| 82 | int main(int argc, char *argv[]) |
| 83 | { |
| 84 | int ret = 1; |
| 85 | uint64_t base_address = 0; |
| 86 | char *filename = NULL; |
| 87 | char *arch = NULL; |
| 88 | void *buf = NULL; |
| 89 | size_t buf_size = 0; |
| 90 | int c; |
| 91 | |
| 92 | while ((c = getopt(argc, argv, "ho:a:b:")) != -1) |
| 93 | switch (c) { |
| 94 | case '?': /* falltrough */ |
| 95 | case 'h': |
| 96 | usage(argv); |
| 97 | return 0; |
| 98 | case 'o': |
| 99 | filename = optarg; |
| 100 | break; |
| 101 | case 'a': |
| 102 | arch = optarg; |
| 103 | break; |
| 104 | case 'b': |
| 105 | base_address = strtoull(optarg, NULL, 0); |
| 106 | break; |
| 107 | default: |
| 108 | break; |
| 109 | } |
| 110 | |
| 111 | if (!filename) { |
| 112 | fprintf(stderr, "E: Missing filename.\n"); |
| 113 | goto done; |
| 114 | } |
| 115 | if (!arch) { |
| 116 | fprintf(stderr, "E: Missing architecture.\n"); |
| 117 | goto done; |
| 118 | } else if (strcmp(arch, "x86_64") != 0) { |
| 119 | fprintf(stderr, "E: Unsupported architecture.\n"); |
| 120 | goto done; |
| 121 | } |
| 122 | if (base_address & 4095) { |
| 123 | fprintf(stderr, "E: Base address not 4 KiB aligned\n"); |
| 124 | goto done; |
| 125 | } |
| 126 | |
| 127 | /* FIXME: Identity map 4GiB for now, increase if necessary */ |
| 128 | if (strcmp(arch, "x86_64") == 0) |
| 129 | ret = gen_pgtbl_x86_64(base_address, 4, &buf, &buf_size); |
| 130 | |
| 131 | if (ret) { |
| 132 | fprintf(stderr, "Failed to generate page tables\n"); |
| 133 | goto done; |
| 134 | } |
| 135 | |
| 136 | // write the table |
| 137 | FILE *fd = fopen(filename, "wb"); |
| 138 | if (!fd) { |
| 139 | fprintf(stderr, "%s open failed: %s\n", filename, strerror(errno)); |
| 140 | goto done; |
| 141 | } |
| 142 | |
| 143 | if (fwrite(buf, 1, buf_size, fd) != buf_size) { |
| 144 | fprintf(stderr, "%s write failed: %s\n", filename, strerror(errno)); |
| 145 | fclose(fd); |
| 146 | goto done; |
| 147 | } |
| 148 | |
| 149 | if (fclose(fd)) { |
| 150 | fprintf(stderr, "%s close failed: %s\n", filename, strerror(errno)); |
| 151 | goto done; |
| 152 | } |
| 153 | |
| 154 | ret = 0; |
| 155 | done: |
| 156 | free(buf); |
| 157 | return ret; |
| 158 | } |