[cpu_uninstall_idmap]
252 /* 253 * TTBR0 is only used for the identity mapping at this stage. Make it 254 * point to zero page to avoid speculatively fetching new entries. 255 */ 256 cpu_uninstall_idmap();
TTBR0_ELn (Translation Table Base Register 0)
Holds the base address of translation table 0, and information
about the memory it occupies. This is one of the translation
tables for the stage 1 translation of memory accesses at ELn.
TTBR0儲存PGD的address, 因此現階段一律將TTBR0指到zero page
detail以後有空再trace
[keep going]
detail以後有空再trace
[keep going]
258 xen_early_init(); 259 efi_init(); 260 arm64_memblock_init();
xen_early_init():
處理hypervisor的device tree (more see Documentation/devicetree/bindings/arm/xen.txt for the 258 * documentation of the Xen Device Tree format.)
efi_init():
跟UEFI related 的operations --> skip
arm64_memblock_init():
在系統初始化階段, Linux 的memory 機制尚未啟動(buddy), 因此系統init初期是使用memblock 機制來分配physical memory 給大家使用
more can see reference as follows
(1) http://www.maxwellxxx.com/linuxmemblock
(2) http://blog.jobbole.com/88452/
(3) http://lib.csdn.net/article/linux/45665
262 paging_init(); 263 264 acpi_table_upgrade(); 265 266 /* Parse the ACPI tables for possible boot-time configuration */ 267 acpi_boot_table_init(); 268 269 if (acpi_disabled) 270 unflatten_device_tree(); 271 272 bootmem_init();
paging_init():
set up page tables (initialize paging mechanism)
acpi_table_upgrade():
acpi_boot_table_init():
skip
bootmem_init():
initialize memory management.
more can see reference as follows
(1) http://www.voidcn.com/blog/gatieme/article/p-6183366.html
274 kasan_init(); 275 276 request_standard_resources(); 277 278 early_ioremap_reset(); 279 280 if (acpi_disabled) 281 psci_dt_init(); 282 else 283 psci_acpi_init(); 284 285 cpu_read_bootcpu_ops(); 286 smp_init_cpus(); 287 smp_build_mpidr_hash(); 288 289 #ifdef CONFIG_VT 290 #if defined(CONFIG_VGA_CONSOLE) 291 conswitchp = &vga_con; 292 #elif defined(CONFIG_DUMMY_CONSOLE) 293 conswitchp = &dummy_con; 294 #endif 295 #endif 296 if (boot_args[1] || boot_args[2] || boot_args[3]) { 297 pr_err("WARNING: x1-x3 nonzero in violation of boot protocol:\n" 298 "\tx1: %016llx\n\tx2: %016llx\n\tx3: %016llx\n" 299 "This indicates a broken bootloader or old kernel\n", 300 boot_args[1], boot_args[2], boot_args[3]); 301 }
kasan_init():
kernel address sanitizer
是一個可以dynamic memory error detector
more can see the reference as follows
(1) http://www.ibm.com/developerworks/cn/linux/1608_tengr_kasan/index.html
(2) http://lxr.free-electrons.com/source/Documentation/kasan.txt
request_standard_resource():
每一個cpu上掛的device 會用一個struct resource 記錄這個device需要使用的physical address(iomem)的info (eg. 起始位置)
more can see the reference as follows
(1) http://blog.csdn.net/boarmy/article/details/8652763
early_ioremap_reset():
set after_paging_init = 1
psci_dt_init():
acpi_disabled =1 被define在 arch/arm64/kernel/acpi.c, line 38
grep .config 也沒有發現CONFIG_PSCI
因此會走psci_dt_init()
618 static const struct of_device_id psci_of_match[] __initconst = { 619 { .compatible = "arm,psci", .data = psci_0_1_init}, 620 { .compatible = "arm,psci-0.2", .data = psci_0_2_init}, 621 { .compatible = "arm,psci-1.0", .data = psci_0_2_init}, 622 {}, 623 };
625 int __init psci_dt_init(void) 626 { 627 struct device_node *np; 628 const struct of_device_id *matched_np; 629 psci_initcall_t init_fn; 630 631 np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np); 632 633 if (!np) 634 return -ENODEV; 635 636 init_fn = (psci_initcall_t)matched_np->data; 637 return init_fn(np); 638 }
of_find_matching_node_and_match() 會到device tree中search 有無與 psci_of_match[] 中定義的element 一樣的node (根據compatible), 若有找到, 將 matched_np 指向psci_of_match[]中的element
for example, we got compatible="arm,psci-0.2" in our device tree, 因此match_np指向psci_of_match[1], 代表目前使用的是psci 0.2 版
接著將matched_np->data 轉成function pointer
然後執行psci_0_2_init, 同時帶入參數np
549 typedef int (*psci_initcall_t)(const struct device_node *); 550 551 /* 552 * PSCI init function for PSCI versions >=0.2 553 * 554 * Probe based on PSCI PSCI_VERSION function 555 */ 556 static int __init psci_0_2_init(struct device_node *np) 557 { 558 int err; 559 560 err = get_set_conduit_method(np); 561 562 if (err) 563 goto out_put_node; 564 /* 565 * Starting with v0.2, the PSCI specification introduced a call 566 * (PSCI_VERSION) that allows probing the firmware version, so 567 * that PSCI function IDs and version specific initialization 568 * can be carried out according to the specific version reported 569 * by firmware 570 */ 571 err = psci_probe(); 572 573 out_put_node: 574 of_node_put(np); 575 return err; 576 }
device tree will define psci node like this
psci {
compatible = "arm, psci-0.2";
method = "smc";
}
(http://lxr.free-electrons.com/source/arch/arm64/boot/dts/mediatek/mt6795.dtsi)
get_set_conduit_method() 會到device tree找這個node
用of_property_read_string(np, "method", &method)找 method
用of_property_read_string(np, "method", &method)找 method
由node可知, psci在這邊是用smc的方式進入ATF, 因此會將invoke_psci_fn設定為 __invoke_psci_fn_smc
部分的get_set_conduit_method() code如下
if (!strcmp("hvc", method)) { 225 invoke_psci_fn = __invoke_psci_fn_hvc; 226 } else if (!strcmp("smc", method)) { 227 invoke_psci_fn = __invoke_psci_fn_smc;invoke_psci_fn是一個function pointer定義如下
64 typedef unsigned long (psci_fn)(unsigned long, unsigned long, 65 unsigned long, unsigned long); 66 static psci_fn *invoke_psci_fn;回到psci_0_2_init()
接著執行psci_probe()
524 static int __init psci_probe(void) 525 { 526 u32 ver = psci_get_version(); 527 528 pr_info("PSCIv%d.%d detected in firmware.\n", 529 PSCI_VERSION_MAJOR(ver), 530 PSCI_VERSION_MINOR(ver)); 531 532 if (PSCI_VERSION_MAJOR(ver) == 0 && PSCI_VERSION_MINOR(ver) < 2) { 533 pr_err("Conflicting PSCI version detected.\n"); 534 return -EINVAL; 535 } 536 537 psci_0_2_set_functions(); 538 539 psci_init_migrate(); 540 541 if (PSCI_VERSION_MAJOR(ver) >= 1) { 542 psci_init_cpu_suspend(); 543 psci_init_system_suspend(); 544 } 545 546 return 0; 547 }
(1) psci_get_version()
call invoke_psci_fn (也就是__invoke_psci_fn_smc()) get firmware version
(2) psci_0_2_set_functions()
496 static void __init psci_0_2_set_functions(void) 497 { 498 pr_info("Using standard PSCI v0.2 function IDs\n"); 499 psci_function_id[PSCI_FN_CPU_SUSPEND] = 500 PSCI_FN_NATIVE(0_2, CPU_SUSPEND); 501 psci_ops.cpu_suspend = psci_cpu_suspend; 502 503 psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF; 504 psci_ops.cpu_off = psci_cpu_off; 505 506 psci_function_id[PSCI_FN_CPU_ON] = PSCI_FN_NATIVE(0_2, CPU_ON); 507 psci_ops.cpu_on = psci_cpu_on; 508 509 psci_function_id[PSCI_FN_MIGRATE] = PSCI_FN_NATIVE(0_2, MIGRATE); 510 psci_ops.migrate = psci_migrate; 511 512 psci_ops.affinity_info = psci_affinity_info; 513 514 psci_ops.migrate_info_type = psci_migrate_info_type; 515 516 arm_pm_restart = psci_sys_reset; 517 518 pm_power_off = psci_sys_poweroff; 519 }
首先psci.c裡面有define
enum psci_function { PSCI_FN_CPU_SUSPEND,
PSCI_FN_CPU_ON,
PSCI_FN_CPU_OFF,
PSCI_FN_MIGRATE,
PSCI_FN_MAX,
};
static u32 psci_function_id[PSCI_FN_MAX];
psci有四種function, 這些function各有id define在上面的enum裡
另外還有 PSCI_FN_NATIVE 這個macro,
另外還有 PSCI_FN_NATIVE 這個macro,
32 /* 33 * While a 64-bit OS can make calls with SMC32 calling conventions, for some 34 * calls it is necessary to use SMC64 to pass or return 64-bit values. 35 * For such calls PSCI_FN_NATIVE(version, name) will choose the appropriate 36 * (native-width) function ID. 37 */ 38 #ifdef CONFIG_64BIT 39 #define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN64_##name 40 #else 41 #define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN_##name 42 #endif
我們有define CONFIG_64BIT
因此macro 炸開會變成 PSCI_0_2_FN64_[NAME]
另外還有一個重要的params
struct psci_operations psci_ops;
因此macro 炸開會變成 PSCI_0_2_FN64_[NAME]
另外還有一個重要的params
struct psci_operations psci_ops;
這是一個psci_operations, struct定義如下
回到psci_0_2_set_functions()會發現這個function一開始會設定大家的suspend/on/off/migrate 的function id, 以及掛上callback functions
深入進去看
關於function id 定義在http://lxr.free-electrons.com/source/include/uapi/linux/psci.h#L34
27 struct psci_operations { 28 int (*cpu_suspend)(u32 state, unsigned long entry_point); 29 int (*cpu_off)(u32 state); 30 int (*cpu_on)(unsigned long cpuid, unsigned long entry_point); 31 int (*migrate)(unsigned long cpuid); 32 int (*affinity_info)(unsigned long target_affinity, 33 unsigned long lowest_affinity_level); 34 int (*migrate_info_type)(void); 35 };
回到psci_0_2_set_functions()會發現這個function一開始會設定大家的suspend/on/off/migrate 的function id, 以及掛上callback functions
深入進去看
psci_function_id[PSCI_FN_CPU_SUSPEND] =PSCI_0_2_CPU_SUSPEND
psci_ops.cpu_suspend = psci_cpu_suspend;
(WHERE defines) PSCI_0_2_CPU_SUSPEND ??
psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF
psci_ops.cpu_off = psci_cpu_off;
psci_function_id[PSCI_FN_CPU_ON] = PSCI_FN_0_2,_CPU_ON
psci_ops.cpu_on = psci_cpu_on;
psci_function_id[PSCI_FN_MIGRATE] = PSCI_FN_0_2_MIGRATE
psci_ops.migrate = psci_migrate;
psci_ops.affinity_info = psci_affinity_info;
psci_ops.migrate_info_type = psci_migrate_info_type;
關於function id 定義在http://lxr.free-electrons.com/source/include/uapi/linux/psci.h#L34
24 /* PSCI v0.2 interface */ 25 #define PSCI_0_2_FN_BASE 0x84000000 26 #define PSCI_0_2_FN(n) (PSCI_0_2_FN_BASE + (n)) 27 #define PSCI_0_2_64BIT 0x40000000 28 #define PSCI_0_2_FN64_BASE \ 29 (PSCI_0_2_FN_BASE + PSCI_0_2_64BIT) 30 #define PSCI_0_2_FN64(n) (PSCI_0_2_FN64_BASE + (n)) 31 32 #define PSCI_0_2_FN_PSCI_VERSION PSCI_0_2_FN(0) 33 #define PSCI_0_2_FN_CPU_SUSPEND PSCI_0_2_FN(1) 34 #define PSCI_0_2_FN_CPU_OFF PSCI_0_2_FN(2) 35 #define PSCI_0_2_FN_CPU_ON PSCI_0_2_FN(3) 36 #define PSCI_0_2_FN_AFFINITY_INFO PSCI_0_2_FN(4) 37 #define PSCI_0_2_FN_MIGRATE PSCI_0_2_FN(5) 38 #define PSCI_0_2_FN_MIGRATE_INFO_TYPE PSCI_0_2_FN(6) 39 #define PSCI_0_2_FN_MIGRATE_INFO_UP_CPU PSCI_0_2_FN(7) 40 #define PSCI_0_2_FN_SYSTEM_OFF PSCI_0_2_FN(8) 41 #define PSCI_0_2_FN_SYSTEM_RESET PSCI_0_2_FN(9) 42 43 #define PSCI_0_2_FN64_CPU_SUSPEND PSCI_0_2_FN64(1) 44 #define PSCI_0_2_FN64_CPU_ON PSCI_0_2_FN64(3) 45 #define PSCI_0_2_FN64_AFFINITY_INFO PSCI_0_2_FN64(4) 46 #define PSCI_0_2_FN64_MIGRATE PSCI_0_2_FN64(5) 47 #define PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU PSCI_0_2_FN64(7) 48 49 #define PSCI_1_0_FN_PSCI_FEATURES PSCI_0_2_FN(10) 50 #define PSCI_1_0_FN_SYSTEM_SUSPEND PSCI_0_2_FN(14) 51 52 #define PSCI_1_0_FN64_SYSTEM_SUSPEND PSCI_0_2_FN64(14)
所有的function id 都是psci base + idx
我看的是64bit的case
因此 psci base = PSCI_0_2_FN64_BASE = 0xC4000000
psci version id = base + 0
cpu_suspend = base + 1
依此類推
另外還有兩個function pointers: arm_pm_restart, pm_power_off
我看的是64bit的case
因此 psci base = PSCI_0_2_FN64_BASE = 0xC4000000
psci version id = base + 0
cpu_suspend = base + 1
依此類推
另外還有兩個function pointers: arm_pm_restart, pm_power_off
分別hook 上psci_sys_reset, psci_sys_poweroff
指定reset & poweroff時要做什麼事
arm_pm_restart = psci_sys_reset;
pm_power_off = psci_sys_poweroff;
(3) psci_init_migrate()
skip
(4) psci_init_cpu_suspend()
而要看cpu_suspend是否存在, 要看psci_features return的64bit value
return value 的format 如下
if Func ID 是 0x84000001(SMC32)/0xC4000001 (SMC64) for CPU_SUSPEND
Bits[31:2] : 若存在, must be 0
Bits[1:1]: 0 if the implementation uses original format for the power_state parameter
1 if the implementation uses the new extended StateID format for the power_state param
Bits[0:0]: 0 if not support os initiated mode
1 if support os initiated mode
else
its[31:0] : 若存在, must be 0
return value 會跟 PSCI_RET_NOT_SUPPORTET(-1)比對, 若不為-1代表存在就將 feature assign 給 psci_cpu_suspend 這個variable
進一步看一下psci_features (第一個參數是psci_feature本身的function id, 第二個參數是欲查詢的function id
(4) psci_init_cpu_suspend()
448 static void __init psci_init_cpu_suspend(void) 449 { 450 int feature = psci_features(psci_function_id[PSCI_FN_CPU_SUSPEND]); 451 452 if (feature != PSCI_RET_NOT_SUPPORTED) 453 psci_cpu_suspend_feature = feature; 454 }主要是將 cpu suspend 的function id 帶入 psci features function, 檢查cpu suspend 的function handler 是否存在
而要看cpu_suspend是否存在, 要看psci_features return的64bit value
return value 的format 如下
if Func ID 是 0x84000001(SMC32)/0xC4000001 (SMC64) for CPU_SUSPEND
Bits[31:2] : 若存在, must be 0
Bits[1:1]: 0 if the implementation uses original format for the power_state parameter
1 if the implementation uses the new extended StateID format for the power_state param
Bits[0:0]: 0 if not support os initiated mode
1 if support os initiated mode
else
its[31:0] : 若存在, must be 0
return value 會跟 PSCI_RET_NOT_SUPPORTET(-1)比對, 若不為-1代表存在就將 feature assign 給 psci_cpu_suspend 這個variable
進一步看一下psci_features (第一個參數是psci_feature本身的function id, 第二個參數是欲查詢的function id
245 static int __init psci_features(u32 psci_func_id) 246 { 247 return invoke_psci_fn(PSCI_1_0_FN_PSCI_FEATURES, 248 psci_func_id, 0, 0); 249 }psci_features 裡面其實是個smc call, invoke_psci_fn相當於__invoke_psci_fn_smc
如下, __invoke_psci_fn_smc裡面call arm_smccc_smc才會真正做一次smc call 到EL3, 第一個參數為function id, 後面有三個arg 可以將info帶入到smc call
123 static unsigned long __invoke_psci_fn_smc(unsigned long function_id, 124 unsigned long arg0, unsigned long arg1, 125 unsigned long arg2) 126 { 127 struct arm_smccc_res res; 128 129 arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); 130 return res.a0; 131 }
arm_smccc_smc() 定義在http://lxr.free-electrons.com/source/include/linux/arm-smccc.h#L84
底層的EL3 應該是assembly code, 因此這裡加上gcc compile conigs asmlinkage
並且將要傳遞的參數存入register 0-7
74 /** 75 * arm_smccc_smc() - make SMC calls 76 * @a0-a7: arguments passed in registers 0 to 7 77 * @res: result values from registers 0 to 3 78 * 79 * This function is used to make SMC calls following SMC Calling Convention. 80 * The content of the supplied param are copied to registers 0 to 7 prior 81 * to the SMC instruction. The return values are updated with the content 82 * from register 0 to 3 on return from the SMC instruction. 83 */ 84 asmlinkage void arm_smccc_smc(unsigned long a0, unsigned long a1, 85 unsigned long a2, unsigned long a3, unsigned long a4, 86 unsigned long a5, unsigned long a6, unsigned long a7, 87 struct arm_smccc_res *res);
另外smc call 的results會被存在 res 裡
63 /** 64 * struct arm_smccc_res - Result from SMC/HVC call 65 * @a0-a3 result values from registers 0 to 3 66 */ 67 struct arm_smccc_res { 68 unsigned long a0; 69 unsigned long a1; 70 unsigned long a2; 71 unsigned long a3; 72 };(5) psci_init_system_suspend()
跟cpu suspend init做一樣的動作
到這裡psci_probe 結束
回到psci_0_2_init
再回到 psci_dt_init結束
最後終於回到setup_arch....