#ifndef __ASSEMBLY__

#define __pa(x)                 __virt_to_phys((unsigned long)(x))
#define __va(x)                 ((void *)__phys_to_virt((phys_addr_t)(x)))


这里用了宏__ASSEMBLY__, 也就是说上面的定义用于非汇编代码里。查了下arm64/.S没有__pa的定义和使用,这里的宏是不是多余的。


 * Physical vs virtual RAM address space conversion.  These are
 * private definitions which should NOT be used outside memory.h
 * files.  Use virt_to_phys/phys_to_virt/__pa/__va instead.
#define __virt_to_phys(x)       (((phys_addr_t)(x) - PAGE_OFFSET + PHYS_OFFSET))
#define __phys_to_virt(x)       ((unsigned long)((x) - PHYS_OFFSET + PAGE_OFFSET))


看下内核文档的说明, 在Documentation/arm/Porting里:


   Physical start address of the first bank of RAM.


   Virtual start address of the first bank of RAM.  During the kernel
   boot phase, virtual address PAGE_OFFSET will be mapped to physical
   address PHYS_OFFSET, along with any other mappings you supply.
   This should be the same value as TASK_SIZE.


 * PAGE_OFFSET - the virtual address of the start of the kernel image (top
 *               (VA_BITS - 1))
 * VA_BITS - the maximum number of bits for virtual addresses.
 * VA_START - the first kernel virtual address.
 * TASK_SIZE - the maximum size of a user space task.
 * TASK_UNMAPPED_BASE - the lower boundary of the mmap VM area.
 * The module space lives between the addresses given by TASK_SIZE
 * and PAGE_OFFSET - it must be within 128MB of the kernel text.
#define VA_BITS                 (CONFIG_ARM64_VA_BITS)
#define VA_START                (UL(0xffffffffffffffff) << VA_BITS)
#define PAGE_OFFSET             (UL(0xffffffffffffffff) << (VA_BITS - 1))

CONFIG_ARM64_VA_BITS定义在arch/arm64/Kconfig里, 我这里高通39bits。

PHYS_OFFSET的定义稍微复杂, 先来看:

#ifndef __ASSEMBLY__
extern phys_addr_t              memstart_addr;
/* PHYS_OFFSET - the physical address of the start of memory. */
#define PHYS_OFFSET             ({ memstart_addr; })



        .quad   __mmap_switched
        .quad   __bss_start                     // x6
        .quad   __bss_stop                      // x7
        .quad   processor_id                    // x4
        .quad   __fdt_pointer                   // x5
        .quad   memstart_addr                   // x6
        .quad   init_thread_union + THREAD_START_SP // sp
 * The following fragment of code is executed with the MMU on in MMU mode, and
 * uses absolute addresses; this is not position independent.
        adr     x3, __switch_data + 8 

        ldp     x6, x7, [x3], #16 
1:      cmp     x6, x7
        b.hs    2f  
        str     xzr, [x6], #8                   // Clear BSS
        b       1b  
        ldp     x4, x5, [x3], #16               // me: x4=processor_id x5=__fdt_pointer
        ldr     x6, [x3], #8                    // me: x6=memstart_addr
        ldr     x16, [x3]
        mov     sp, x16 
        str     x22, [x4]                       // Save processor ID
        str     x21, [x5]                       // Save FDT pointer
        str     x24, [x6]                       // Save PHYS_OFFSET
        mov     x29, #0
        bl      kasan_early_init
        b       start_kernel
        ldr     x27, __switch_data              // address to jump to after
                                                // MMU has been enabled
        adrp    lr, __enable_mmu                // return (PIC) address
        add     lr, lr, #:lo12:__enable_mmu
        ldr     x12, [x23, #CPU_INFO_SETUP]
        add     x12, x12, x28                   // __virt_to_phys
        br      x12                             // initialise processor

由上可以看出:__enable_mmu -> __mmap_switched -> start_kernel,在__mmap_switched里保存了memstart_addr

ok, 所以还是要搞清楚x24的来源。

看下entry, arch/arm64/kernel/head.S:

        mov     x21, x0                         // x21=FDT
        bl      el2_setup                       // Drop to EL1, w20=cpu_boot_mode
        bl      __calc_phys_offset              // x24=PHYS_OFFSET, x28=PHYS_OFFSET-PAGE_OFFSET

 * Calculate the start of physical memory.
        adr     x0, 1f                          
        ldp     x1, x2, [x0]                    
        sub     x28, x0, x1                     // x28 = PHYS_OFFSET - PAGE_OFFSET
        add     x24, x2, x28                    // x24 = PHYS_OFFSET

        .align 3
1:      .quad   .   
        .quad   PAGE_OFFSET

没有看明白上面计算方法主要是因为.quad .

先看下out下, 能看出这里都是虚拟地址了。

     6 ffffffc000080000 T _text
     7 ffffffc000080000 t efi_head
     8 ffffffc000080040 t pe_header
     9 ffffffc000080044 t coff_header
    10 ffffffc000080058 t optional_header
    11 ffffffc000080070 t extra_header_fields
    12 ffffffc0000800f8 t section_table
    13 ffffffc000081000 T stext


[2180] booting linux @ 0x80080000, ramdisk @ 0x83600000 (2209319), tags/device tree @ 0x83400000

这里jump addr就是0x80080000, ddr base is 0x80000000, 偏移是0x80000从kernel/arch/arm64也能看出来:

# The byte offset of the kernel image in RAM from the start of RAM.
TEXT_OFFSET := $(shell awk 'BEGIN {srand(); printf "0x%03x000\n", int(512 * rand())}')
TEXT_OFFSET := 0x00080000


arm64 code is based on arm32, 查下git log:

commit 72a20e22f49e2dad3180c23980a9df1c63faab0a
Author: Russell King <>
Date:   Tue Jan 4 19:04:00 2011 +0000

    ARM: P2V: eliminate head.S use of PHYS_OFFSET for !XIP_KERNEL
    head.S makes use of PHYS_OFFSET.  When it becomes a variable, the
    assembler won't understand this.  Compute PHYS_OFFSET by the following
    method.  This code is linked at its virtual address, but run at before
    the MMU is enabled, so at his physical address.

    1:  .long   .
        .long   PAGE_OFFSET
        adr     r0, 1b          @ r0 = physical ','
        ldmia   r0, {r1, r2}    @ r1 = virtual '.', r2 = PAGE_OFFSET
        sub     r1, r0, r1      @ r1 = physical-virtual
        add     r2, r2, r1      @ r2 = PAGE_OFFSET + physical-virtual
                                @    := PHYS_OFFSET.
    Switch XIP users of PHYS_OFFSET to use PLAT_PHYS_OFFSET - we can't
    use this method for XIP kernels as the code doesn't execute in RAM.

    Tested-by: Tony Lindgren <>
    Reviewed-by: Nicolas Pitre <>
    Signed-off-by: Russell King <>


看注释r0 = physical '.', r1 = virtual '.', 也就是r0是1b(backward)的物理地址,r1是1b的虚拟地址

GUN as man对.long .的说明:

5.4 The Special Dot Symbol

The special symbol . refers to the current address that as is assembling into. Thus, the expression melvin: .long . defines melvin to contain its own address. Assigning a value to . is treated the same as a .org directive. Thus, the expression .=.+4 is the same as saying .space 4.

这里提到就是链接地址也就是虚拟地址,而adr x0, 1b, 此时的环境没有打开mmu所以此时是物理地址。回过头就能理解git的注释 - This code is linked at its virtual address, but run at before the MMU is enabled, so at his physical address