arm64/kernel3.18的定义在arch/arm64/include/asm/memory.h里

#ifndef __ASSEMBLY__

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

#endif

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

继续grep。

/*
* 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))

这里就没有用__ASSEMBLY__

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

PHYS_OFFSET
Physical start address of the first bank of RAM.

PAGE_OFFSET
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代码定义:

/*
* 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; })

arm64/*.S没有用到PHYS_OFFSET,应该没必要用__ASSEMBLY__

memstart_addr在arch/arm64/kernel/head.S:

__switch_data:
.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.
*/
__mmap_switched:
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
2:
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
#ifdef CONFIG_KASAN
bl kasan_early_init
#endif
b start_kernel
ENDPROC(__mmap_switched)
        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
ENDPROC(stext)

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

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

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

ENTRY(stext)
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.
*/
__calc_phys_offset:
adr x0, 1f
ldp x1, x2, [x0]
sub x28, x0, x1 // x28 = PHYS_OFFSET - PAGE_OFFSET
add x24, x2, x28 // x24 = PHYS_OFFSET
ret
ENDPROC(__calc_phys_offset)

.align 3
1: .quad .
.quad PAGE_OFFSET

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

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

 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

看下BL/LK的跳转地址log:

[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.
ifeq ($(CONFIG_ARM64_RANDOMIZE_TEXT_OFFSET), y)
TEXT_OFFSET := $(shell awk 'BEGIN {srand(); printf "0x%03x000\n", int(512 * rand())}')
else
TEXT_OFFSET := 0x00080000
endif

加了个打印看到memstart_addr是0x80000000。

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

commit 72a20e22f49e2dad3180c23980a9df1c63faab0a
Author: Russell King <rmk+kernel@arm.linux.org.uk>
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 <tony@atomide.com>
Reviewed-by: Nicolas Pitre <nicolas.pitre@linaro.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

这个修改就是把head.S里对PHYS_OFFSET的使用改成动态计算。

看注释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

refer:

https://www.sourceware.org/binutils/docs-2.12/as.info/