|
13 | 13 | #ifndef KASLD_API_H |
14 | 14 | #define KASLD_API_H |
15 | 15 |
|
16 | | -#define PAGE_SIZE 0x1000ul |
| 16 | +/* The 4 KiB granule the kernel's own layout arithmetic is written in: the unit |
| 17 | + * behind expressions lifted from kernel source, such as x86_64's module |
| 18 | + * randomization span of `1024 * PAGE_SIZE`. It is a fixed number that stays 4 |
| 19 | + * KiB whatever the analysed machine runs. |
| 20 | + * |
| 21 | + * It is NOT the target's page size, and must never stand in for one. Four of |
| 22 | + * the eight supported architecture families admit several page sizes -- arm64 |
| 23 | + * and loongarch64 4/16/64 KiB, mips 4/8/16/32/64 KiB, powerpc 4/16/64/256 KiB |
| 24 | + * -- so on those a page-frame number converted with this constant is wrong by |
| 25 | + * up to 64x. Where the target's page size is genuinely needed, it comes from |
| 26 | + * the SF_PAGE_SIZE observation at runtime; pfn_to_phys() below exists only on |
| 27 | + * the architectures where the two coincide, so a conversion that needs the |
| 28 | + * runtime value cannot reach this constant by accident. |
| 29 | + * |
| 30 | + * Deliberately not spelled PAGE_SIZE: that name belongs to the C library on |
| 31 | + * some targets (musl defines it under _GNU_SOURCE wherever the ABI fixes a |
| 32 | + * page size), and a macro named for a machine property invites exactly the |
| 33 | + * substitution the paragraph above forbids. */ |
| 34 | +#define KASLD_LAYOUT_GRANULE 0x1000ul |
17 | 35 | #define KB 0x400ul |
18 | 36 | #define MB 0x100000ul |
19 | 37 | #define GB 0x40000000ul |
@@ -383,6 +401,14 @@ __extension__ _Static_assert((unsigned long)KERNEL_PHYS_MAX > |
383 | 401 | * |
384 | 402 | * Treating them as one flag would silently pick a side on whichever arch |
385 | 403 | * arrives first. */ |
| 404 | +#ifndef PAGE_SIZE_MIN |
| 405 | +#error \ |
| 406 | + "arch header must define PAGE_SIZE_MIN / PAGE_SIZE_MAX (the page sizes the arch admits)" |
| 407 | +#endif |
| 408 | +#ifndef PAGE_SIZE_MAX |
| 409 | +#error \ |
| 410 | + "arch header must define PAGE_SIZE_MIN / PAGE_SIZE_MAX (the page sizes the arch admits)" |
| 411 | +#endif |
386 | 412 | #ifndef DIRECTMAP_STATIC |
387 | 413 | #error "arch header must define DIRECTMAP_STATIC (0 or 1)" |
388 | 414 | #endif |
@@ -778,6 +804,55 @@ static inline unsigned long kasld__directmap_virt_to_phys(unsigned long v) { |
778 | 804 | "the compile-time directmap projection requires PAGE_OFFSET_KNOWN_AT_BUILD" |
779 | 805 | #endif |
780 | 806 |
|
| 807 | +/* ========================================================================= |
| 808 | + * Page-frame number conversion |
| 809 | + * |
| 810 | + * 1 iff this architecture admits exactly one page size, so a page-frame number |
| 811 | + * -- which counts the TARGET kernel's pages -- can be converted to a byte |
| 812 | + * address with a compile-time constant. Derived from the arch header's pair, |
| 813 | + * never declared: an arch cannot then claim a page size it does not admit, and |
| 814 | + * a mistyped axis value fails to compile instead of expanding to 0. |
| 815 | + * |
| 816 | + * Where the edges differ the multiplier is not knowable at build time, and the |
| 817 | + * error is not small: mips and loongarch64 reach 64 KiB and 32-bit powerpc |
| 818 | + * reaches 256 KiB, so a PFN converted at 4 KiB understates a physical address |
| 819 | + * by up to 64x. Understating the top of physical memory is precisely how a |
| 820 | + * bound derived from it lands past truth, which is why this is a gate rather |
| 821 | + * than a comment. |
| 822 | + * ========================================================================= */ |
| 823 | +#define PAGE_SIZE_KNOWN_AT_BUILD (PAGE_SIZE_MIN == PAGE_SIZE_MAX) |
| 824 | + |
| 825 | +#if PAGE_SIZE_KNOWN_AT_BUILD |
| 826 | +/* pfn_to_phys(pfn): first byte of the frame. phys_to_pfn(p): the frame holding |
| 827 | + * `p`. Defined ONLY where the page size is single-valued, so a conversion that |
| 828 | + * needs the target's runtime size cannot silently reach a constant instead: |
| 829 | + * the call does not compile on the arches where the constant would be wrong. |
| 830 | + * Callers must #ifdef and take SF_PAGE_SIZE on the other path. |
| 831 | + * |
| 832 | + * pfn_to_phys returns 0 on overflow rather than a wrapped address, so a caller |
| 833 | + * bounding a window from it cannot be handed a small number for a huge frame. |
| 834 | + */ |
| 835 | +static inline unsigned long kasld__pfn_to_phys(unsigned long pfn) { |
| 836 | + if (pfn > (unsigned long)-1 / (unsigned long)PAGE_SIZE_MIN) |
| 837 | + return 0; |
| 838 | + return pfn * (unsigned long)PAGE_SIZE_MIN; |
| 839 | +} |
| 840 | +static inline unsigned long kasld__phys_to_pfn(unsigned long p) { |
| 841 | + return p / (unsigned long)PAGE_SIZE_MIN; |
| 842 | +} |
| 843 | +#define pfn_to_phys(pfn) kasld__pfn_to_phys((unsigned long)(pfn)) |
| 844 | +#define phys_to_pfn(p) kasld__phys_to_pfn((unsigned long)(p)) |
| 845 | +#endif |
| 846 | + |
| 847 | +/* Self-enforcing restatement of the gate above, in the same shape as the |
| 848 | + * directmap projection's. Trivially satisfied as written; widening the |
| 849 | + * condition to a predicate that does not imply PAGE_SIZE_KNOWN_AT_BUILD breaks |
| 850 | + * the build on the arches where the multiplier would be a guess, rather than |
| 851 | + * quietly producing physical addresses computed from the wrong page size. */ |
| 852 | +#if defined(pfn_to_phys) && !PAGE_SIZE_KNOWN_AT_BUILD |
| 853 | +#error "pfn_to_phys requires PAGE_SIZE_KNOWN_AT_BUILD" |
| 854 | +#endif |
| 855 | + |
781 | 856 | /* The linear-map base where this build genuinely knows it, 0 where it does not. |
782 | 857 | * |
783 | 858 | * For presentation. A renderer stating an address is asserting it, so it may |
|
0 commit comments