具体来说就是smp_setup_processor_id函数中如下几个部分:
set_cpu_logical_map(0, mpidr);设置CPU逻辑地图数组变量。
set_my_cpu_offset(0);设置Per-CPU变量。
pr_info打印相关信息
建立CPU逻辑地图
所谓的CPU逻辑地图其实就是一个cpu-id编号到cpu对应MPIDR_EL1寄存器的数组变量。
在第一段中,通过MRS指令,获取并转储MIDR_EL1寄存器的值到mpidr内存中。在接下来的数组设置中,展开在/arch/arm64/include/asm/smp.h文件下:
/*
* Logical CPU mapping.
*/
extern u64 __cpu_logical_map[NR_CPUS];
extern u64 cpu_logical_map(int cpu);
static inline void set_cpu_logical_map(int cpu, u64 hwid)
{
__cpu_logical_map[cpu] = hwid;
}可以看到, 这个变量最终是被一个外部声明的__cpu_logical_map变量定义的,这个变量存在于/arch/arm64/kernel/setup.c中被具体定义:
u64 __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID };可以看出,在原文中,此时kernel第2段还是仅仅只是追求建立一个cpu=0时的初始化,所以此时这个CPU逻辑地图(即数组中第i个cpu指向其MPIDR_EL1寄存器的描述值)只有一个组的元素。

这样,这里的含义就是获取到CPU0的mpidr寄存器的值,并建立了一个数组。
这种映射关系建立成功后,会通过cpu_logical_map[cpu-id]的方式进行对应MPIDR_EL1寄存器的值的访问。
u64 __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID };
u64 cpu_logical_map(int cpu)
{
return __cpu_logical_map[cpu];
}设置Per-CPU的值
set_my_cpu_offset变量在/arch/arm64/include/asm/percpu.h文件中定义:
static inline void set_my_cpu_offset(unsigned long off)
{
asm volatile(ALTERNATIVE("msr tpidr_el1, %0",
"msr tpidr_el2, %0",
ARM64_HAS_VIRT_HOST_EXTN)
:: "r" (off) : "memory");
}
// 这个是相关的主函数原文:
set_my_cpu_offset(0);汇编揭示出,这个函数的主要作用,就是将tpidr_el1、tpidr_el2寄存器的值设置为off的值,也就是0
TPIDR_EL1寄存器是用于存储当前CPU的Per-CPU数据偏移量的寄存器。这是专门用于保存Per-CPU变量的基地址指针或偏移量寄存器。
此时,还只是单纯的进行偏移量的设置,有关Per-CPU变量的具体用法,等到后面SMP框架搭起来的时候详细说。
pr_info打印函数
这是一个来自printk的函数,在/include/linux/printk.h中可以看到就是printk:
#define pr_info(fmt, ...) \
printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)这里有一个很有意思的问题:
正常情况下,kernel的启动使用earlycon命令启用对应的ttyAMA串口。但是,kernel的启动命令本身需要等到setup_arch函数的时候才会执行command的解析。也就是说,此时在smp_setup_processor_id函数中是并不会存在真正的,由printk导致的串口打印的。
printk函数本身存在于/kernel/printk/printk.c:
// 这里是/kernel/printk/printk.c的核心函数
asmlinkage __visible int printk(const char *fmt, ...)
{
va_list args;
int r;
va_start(args, fmt);
r = vprintk_func(fmt, args);
va_end(args);
return r;
}
EXPORT_SYMBOL(printk);
// 这里是转到vprintk_func的实现/kernel/printk/printk_safe.c
__printf(1, 0) int vprintk_func(const char *fmt, va_list args)
{
#ifdef CONFIG_KGDB_KDB
/* Allow to pass printk() to kdb but avoid a recursion. */
if (unlikely(kdb_trap_printk && kdb_printf_cpu < 0))
return vkdb_printf(KDB_MSGSRC_PRINTK, fmt, args);
#endif
/*
* Try to use the main logbuf even in NMI. But avoid calling console
* drivers that might have their own locks.
*/
if ((this_cpu_read(printk_context) & PRINTK_NMI_DIRECT_CONTEXT_MASK) &&
raw_spin_trylock(&logbuf_lock)) {
int len;
len = vprintk_store(0, LOGLEVEL_DEFAULT, NULL, fmt, args); // 这里打印到环形缓冲区
raw_spin_unlock(&logbuf_lock);
defer_console_output();
return len;
}
/* Use extra buffer in NMI when logbuf_lock is taken or in safe mode. */
if (this_cpu_read(printk_context) & PRINTK_NMI_CONTEXT_MASK)
return vprintk_nmi(fmt, args);
/* Use extra buffer to prevent a recursion deadlock in safe mode. */
if (this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK)
return vprintk_safe(fmt, args);
/* No obstacles. */
return vprintk_default(fmt, args);
}
// 最终,这里是对应存储的代码
int vprintk_store(int facility, int level,
const struct dev_printk_info *dev_info,
const char *fmt, va_list args)
{
static char textbuf[LOG_LINE_MAX];
char *text = textbuf;
size_t text_len;
enum log_flags lflags = 0;
/*
* The printf needs to come first; we need the syslog
* prefix which might be passed-in as a parameter.
*/
text_len = vscnprintf(text, sizeof(textbuf), fmt, args);
/* mark and strip a trailing newline */
if (text_len && text[text_len-1] == '\n') {
text_len--;
lflags |= LOG_NEWLINE;
}
/* strip kernel syslog prefix and extract log level or control flags */
if (facility == 0) {
int kern_level;
while ((kern_level = printk_get_level(text)) != 0) {
switch (kern_level) {
case '0' ... '7':
if (level == LOGLEVEL_DEFAULT)
level = kern_level - '0';
break;
case 'c': /* KERN_CONT */
lflags |= LOG_CONT;
}
text_len -= 2;
text += 2;
}
}
if (level == LOGLEVEL_DEFAULT)
level = default_message_loglevel;
if (dev_info)
lflags |= LOG_NEWLINE;
return log_output(facility, level, lflags, dev_info, text, text_len);
}可以看到,在极早期,只存在一个临时定义的static char textbuf[LOG_LINE_MAX];这样的数组,用来承接对应的打印。