2016年12月1日 星期四

setup_arch(4) - PSCI partial

看完local_async_enable, 繼續 trace setup_arch()囉
[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了, 先來看看這個register在幹嘛? Programmers Guide的定義如下
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]
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
由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, 
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;
這是一個psci_operations, struct定義如下
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
分別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()
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....

全國推廣動物認領養平台串聯貼紙

全國推廣動物認領養平台串聯貼紙