arm64_linux启动流程分析02_保存启动信息

解上节, 我们先来看看bl preserve_boot_args

1
2
3
4
5
6
7
8
9
10
11
12
13
preserve_boot_args:
mov x21, x0 // x21=FDT

adr_l x0, boot_args // record the contents of
stp x21, x1, [x0] // x0 .. x3 at kernel entry
stp x2, x3, [x0, #16]

dmb sy // needed before dc ivac with
// MMU off

mov x1, #0x20 // 4 x 8 bytes
b __inval_dcache_area // tail call
ENDPROC(preserve_boot_args)

代码的含义一目了然, 把存fdt内存地址的x0保存到x21寄存器. 然后把启动参数x0, x1, x2, x3全部保存到boot_args数组中.

arm64 linux规定:

Primary CPU general-purpose register settings

x0 = physical address of device tree blob (dtb) in system RAM.

x1 = 0 (reserved for future use)

x2 = 0 (reserved for future use)

x3 = 0 (reserved for future use)

这里值得注意的有几点

  1. 这里有用到adr_l, arm64并没有这个指令, 这是一个宏
1
2
3
4
5
6
7
8
9
10
11
	.macro	adr_l, dst, sym
#ifndef MODULE
adrp \dst, \sym
add \dst, \dst, :lo12:\sym
#else
movz \dst, #:abs_g3:\sym
movk \dst, #:abs_g2_nc:\sym
movk \dst, #:abs_g1_nc:\sym
movk \dst, #:abs_g0_nc:\sym
#endif
.endm

可以看到, 这里的adr_l拆分成了两条指令, adrp + add, adrp指令最大寻址空间时+-4GB, 但是所寻址的地址是4KB对齐的. 所以这里在加了一个add指令来修正地址的低12bit, 从而实现了这个加载+-4GB任意位置的运行时地址的宏.

  1. __inval_dcache_area函数用来invalidate指定区域的dcache, 具体如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
ENTRY(__inval_dcache_area)
/* FALLTHROUGH */

/*
* __dma_inv_area(start, size)
* - start - virtual start address of region
* - size - size in question
*/
__dma_inv_area:
add x1, x1, x0
dcache_line_size x2, x3
sub x3, x2, #1
tst x1, x3 // end cache line aligned?
bic x1, x1, x3
b.eq 1f
dc civac, x1 // clean & invalidate D / U line
1: tst x0, x3 // start cache line aligned?
bic x0, x0, x3
b.eq 2f
dc civac, x0 // clean & invalidate D / U line
b 3f
2: dc ivac, x0 // invalidate D / U line
3: add x0, x0, x2
cmp x0, x1
b.lo 2b
dsb sy
ret
ENDPIPROC(__inval_dcache_area)

可以看到如果指定内存区域有跨越cacheline, 那么对两边跨越了cacheline的地址使用的clean + invalidate, 对于中间区域可以直接invalidate不用写回内存, 从而加快invalidate速度.