Hi, after traveling several kernel areas (for more or less related reasons), it's now clear that you got no other choice than to introduce phys_offset. (I let your reasoning for it mislead me before. Sorry.) Two conditions must be met: 1) __pfn_to_page(x) == &mem_map[x - ARCH_PFN_OFFSET] 2) PAGE_OFFSET is not "the start of the kernel virtual address space", but the virtual address of the first page, handled by the table, as is set in free_area_init(): __pfn_to_page(__pa(PAGE_OFFSET) >> PAGE_SHIFT) == &mem_map[0] These are met if and only if __pa(PAGE_OFFSET) == ARCH_PFN_OFFSET<<PAGE_SHIFT + r, 0 <= r < 1<<PAGE_SHIFT Using the simpliest arithmetic formula for __pa() "__pa(x) := x - PAGE_OFFSET + d", we get d = ARCH_PFN_OFFSET << PAGE_SHIFT, and use the descriptive name PHYS_OFFSET for this "d". (Of course, i could have guessed and tried and defined PHYS_OFFSET in the first place...) On the other hand, "__pa(kseg) == 0" must be met for kseg in {KSEG0,KSEG1, CKSEG0,CKSEG1,any XKPHYS-region}. This enforces: PAGE_OFFSET = kseg + (ARCH_PFN_OFFSET << PAGE_SHIFT) Perhaps you could have pointed to the above relations, and thus saved several "users" from a bit of trial and error :-) My suggestion, to use the detected offset (min_low_pfn), instead of a predefined constant, may be too expensive. The flexibility gained by "#define ARCH_PFN_OFFSET min_low_pfn" and hence "#define PHYS_OFFSET (min_low_pfn << PAGE_SHIFT)" is probably not worth the cost, that arises when these must be evaluated in so many places. kind regards peter