|
8 | 8 | // fields via kasld_fopen), so they honour KASLD_SYSROOT redirection. |
9 | 9 | // |
10 | 10 | // Read by the engine bridge, which emits these as PHYS RAM extents that the |
11 | | -// firmware_memmap_holes verdict consumes. |
| 11 | +// firmware_memmap_holes verdict consumes. The loader is all-or-nothing: it |
| 12 | +// returns -1 (caller emits nothing) if the map cannot be captured completely, |
| 13 | +// because a partial covering would fabricate false gaps for gap-carving. |
12 | 14 | // --- |
13 | 15 | // <bcoles@gmail.com> |
14 | 16 |
|
@@ -42,42 +44,74 @@ kasld_memmap_first_line(const char *path, char *buf, size_t len) { |
42 | 44 | return 0; |
43 | 45 | } |
44 | 46 |
|
45 | | -/* Collect "System RAM" inclusive extents; returns count (0 if absent). */ |
| 47 | +/* Collect "System RAM" inclusive extents into out[0..max). |
| 48 | + * |
| 49 | + * Returns the number of extents, or -1 if the firmware map cannot be |
| 50 | + * represented COMPLETELY and faithfully: more than `max` System RAM entries, |
| 51 | + * a numbered entry whose type/start/end could not be read or parsed, or an |
| 52 | + * extent value that does not fit unsigned long (i386/PAE >4 GiB truncation). |
| 53 | + * A covering MUST be complete — a truncated or partial map fabricates false |
| 54 | + * gaps that the gap-carving rules (firmware_memmap_holes, ram_map_phys_exclude) |
| 55 | + * turn into unsound C_EXCLUDEs — so callers MUST emit nothing on -1. Mirrors |
| 56 | + * the all-or-nothing covering_ok guard in boot_params_e820. */ |
46 | 57 | __attribute__((unused)) static int |
47 | 58 | kasld_load_ram_extents(struct kasld_ram_extent *out, int max) { |
48 | 59 | DIR *d = kasld_opendir(KASLD_MEMMAP_BASE); |
49 | 60 | if (!d) |
50 | 61 | return 0; |
51 | | - int n = 0; |
| 62 | + int n = 0, incomplete = 0; |
52 | 63 | struct dirent *ent; |
53 | | - while ((ent = readdir(d)) != NULL && n < max) { |
| 64 | + while ((ent = readdir(d)) != NULL) { |
54 | 65 | if (ent->d_name[0] == '.') |
55 | 66 | continue; |
56 | 67 | char path[512], buf[256]; |
57 | 68 | snprintf(path, sizeof(path), "%s/%s/type", KASLD_MEMMAP_BASE, ent->d_name); |
58 | | - if (kasld_memmap_first_line(path, buf, sizeof(buf)) != 0) |
59 | | - continue; |
| 69 | + if (kasld_memmap_first_line(path, buf, sizeof(buf)) != 0) { |
| 70 | + incomplete = 1; /* numbered entry with no type: map state unknown */ |
| 71 | + break; |
| 72 | + } |
60 | 73 | if (strcmp(buf, "System RAM") != 0) |
61 | | - continue; |
| 74 | + continue; /* a genuine non-RAM region: a real gap, correctly omitted */ |
| 75 | + /* From here the entry IS System RAM; any failure makes the map partial. */ |
| 76 | + if (n >= max) { |
| 77 | + incomplete = 1; /* more RAM entries than out[]: would truncate the map */ |
| 78 | + break; |
| 79 | + } |
62 | 80 | snprintf(path, sizeof(path), "%s/%s/start", KASLD_MEMMAP_BASE, ent->d_name); |
63 | | - if (kasld_memmap_first_line(path, buf, sizeof(buf)) != 0) |
64 | | - continue; |
| 81 | + if (kasld_memmap_first_line(path, buf, sizeof(buf)) != 0) { |
| 82 | + incomplete = 1; |
| 83 | + break; |
| 84 | + } |
65 | 85 | char *endp; |
66 | | - unsigned long start = strtoul(buf, &endp, 16); |
67 | | - if (endp == buf) |
68 | | - continue; |
| 86 | + unsigned long long start = strtoull(buf, &endp, 16); |
| 87 | + if (endp == buf) { |
| 88 | + incomplete = 1; |
| 89 | + break; |
| 90 | + } |
69 | 91 | snprintf(path, sizeof(path), "%s/%s/end", KASLD_MEMMAP_BASE, ent->d_name); |
70 | | - if (kasld_memmap_first_line(path, buf, sizeof(buf)) != 0) |
71 | | - continue; |
72 | | - unsigned long end = strtoul(buf, &endp, 16); |
73 | | - if (endp == buf || end < start) |
74 | | - continue; |
75 | | - out[n].lo = start; |
76 | | - out[n].hi = end; |
| 92 | + if (kasld_memmap_first_line(path, buf, sizeof(buf)) != 0) { |
| 93 | + incomplete = 1; |
| 94 | + break; |
| 95 | + } |
| 96 | + unsigned long long end = strtoull(buf, &endp, 16); |
| 97 | + if (endp == buf || end < start) { |
| 98 | + incomplete = 1; |
| 99 | + break; |
| 100 | + } |
| 101 | + /* On i386 unsigned long is 32-bit; a >4 GiB extent would truncate and |
| 102 | + * corrupt the covering. Suppress the whole map rather than store a wrong |
| 103 | + * extent. */ |
| 104 | + if ((unsigned long long)(unsigned long)start != start || |
| 105 | + (unsigned long long)(unsigned long)end != end) { |
| 106 | + incomplete = 1; |
| 107 | + break; |
| 108 | + } |
| 109 | + out[n].lo = (unsigned long)start; |
| 110 | + out[n].hi = (unsigned long)end; |
77 | 111 | n++; |
78 | 112 | } |
79 | 113 | closedir(d); |
80 | | - return n; |
| 114 | + return incomplete ? -1 : n; |
81 | 115 | } |
82 | 116 |
|
83 | 117 | #endif /* KASLD_FIRMWARE_MEMMAP_H */ |
0 commit comments