mips架构linux启动分析(二)
程序员文章站
2022-07-13 21:48:12
...
在start_kernel函数的开始部分:
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern const struct kernel_param __start___param[], __stop___param[];
lockdep_init();
smp_setup_processor_id();
debug_objects_early_init();
boot_init_stack_canary();
cgroup_init_early();
local_irq_disable();
early_boot_irqs_disabled = true;
boot_cpu_init();
page_address_init();
pr_notice("%s", linux_banner);
setup_arch(&command_line);
......
}
第一个函数:/*
在这里主要初始化了两个表,这两个表具体是做什么,目前还不是很清楚..?????
*/
void lockdep_init(void)
{
int i;
if (lockdep_initialized)
return;
for (i = 0; i < CLASSHASH_SIZE; i++)
INIT_LIST_HEAD(classhash_table + i);
for (i = 0; i < CHAINHASH_SIZE; i++)
INIT_LIST_HEAD(chainhash_table + i);
lockdep_initialized = 1;
}
表的定义:static struct list_head classhash_table[CLASSHASH_SIZE];
static struct list_head chainhash_table[CLASSHASH_SIZE];
第二个:
在这里的 smp_setup_processor_id();函数为空。
第三个:
void __init debug_objects_early_init(void)
{
int i;
for (i = 0; i < ODEBUG_HASH_SIZE; i++)
raw_spin_lock_init(&obj_hash[i].lock);
for (i = 0; i < ODEBUG_POOL_SIZE; i++)
hlist_add_head(&obj_static_pool[i].node, &obj_pool);
}
这里也是初始化了两个hash表,第一个for循环是初始化所有的锁(lock)为一样的,第二个为把锁有的obj_pool加入到obj_static_pool中。
具体功能以及作用还不是很清楚......????????????
第四个:
函数boot_init_stack_canary()为空。
第五个函数:
主要工作就是对那些有需要提前进行一些初始化的子系统进行初始化操作。
int __init cgroup_init_early(void)
{
int i;
atomic_set(&init_css_set.refcount, 1);
INIT_LIST_HEAD(&init_css_set.cg_links);
INIT_LIST_HEAD(&init_css_set.tasks);
INIT_HLIST_NODE(&init_css_set.hlist);
css_set_count = 1;
init_cgroup_root(&rootnode);
root_count = 1;
init_task.cgroups = &init_css_set;
init_css_set_link.cg = &init_css_set;
init_css_set_link.cgrp = dummytop;
list_add(&init_css_set_link.cgrp_link_list,
&rootnode.top_cgroup.css_sets);
list_add(&init_css_set_link.cg_link_list,
&init_css_set.cg_links);
for (i = 0; i < CGROUP_SUBSYS_COUNT; i++) {
struct cgroup_subsys *ss = subsys[i];
/* at bootup time, we don't worry about modular subsystems */
if (!ss || ss->module)
continue;
BUG_ON(!ss->name);
BUG_ON(strlen(ss->name) > MAX_CGROUP_TYPE_NAMELEN);
BUG_ON(!ss->css_alloc);
BUG_ON(!ss->css_free);
if (ss->subsys_id != i) {
printk(KERN_ERR "cgroup: Subsys %s id == %d\n",
ss->name, ss->subsys_id);
BUG();
}
if (ss->early_init)
cgroup_init_subsys(ss);
}
return 0;
}
目前只是清楚这个函数的大概作用,更具体的分析,需要等到自己更了解了。
第六个:
禁止中断:
local_irq_disable();
#define local_irq_disable() do { raw_local_irq_disable(); } while (0)
#define raw_local_irq_disable() arch_local_irq_disable()
static inline void arch_local_irq_disable(void)
{
__asm__ __volatile__(
" .set push \n"
" .set noat \n"
#这条指令就是禁止中断
" di \n"
" " __stringify(__irq_disable_hazard) " \n"
" .set pop \n"
: /* no outputs */
: /* no inputs */
: "memory");
}
第七个:/*
记录cpu状态
*/
static void __init boot_cpu_init(void)
{
int cpu = smp_processor_id();
/* Mark the boot cpu "present", "online" etc for SMP and UP case */
set_cpu_online(cpu, true);
set_cpu_active(cpu, true);
set_cpu_present(cpu, true);
set_cpu_possible(cpu, true);
}
具体是怎么获取cpu的id的呢?
# define smp_processor_id() raw_smp_processor_id()
#define raw_smp_processor_id() (current_thread_info()->cpu)
/*
这里看出来当前运行的任务由28号寄存器直接保存其地址
*/
static inline struct thread_info *current_thread_info(void)
{
register struct thread_info *__current_thread_info __asm__("$28");
return __current_thread_info;
}
可以看出来运行中的任务的id是存储在第28号寄存器中的。这样就方便了直接获取任务的id等相关信息。
设置cpu状态的四个函数:
void set_cpu_online(unsigned int cpu, bool online)
{
if (online)
cpumask_set_cpu(cpu, to_cpumask(cpu_online_bits));
else
cpumask_clear_cpu(cpu, to_cpumask(cpu_online_bits));
}
void set_cpu_active(unsigned int cpu, bool active)
{
if (active)
cpumask_set_cpu(cpu, to_cpumask(cpu_active_bits));
else
cpumask_clear_cpu(cpu, to_cpumask(cpu_active_bits));
}
void init_cpu_present(const struct cpumask *src)
{
cpumask_copy(to_cpumask(cpu_present_bits), src);
}
void init_cpu_possible(const struct cpumask *src)
{
cpumask_copy(to_cpumask(cpu_possible_bits), src);
}
这四个函数的实现,也就是调用的函数分析:
struct cpumask结构体定义:
typedef struct cpumask
{
DECLARE_BITMAP(bits, NR_CPUS);
} cpumask_t;
cpu_online_bit/cpu_active_bits/cpu_present_bits/cpu_possible_bits的定义:static DECLARE_BITMAP(cpu_online_bits, CONFIG_NR_CPUS) __read_mostly;
const struct cpumask *const cpu_online_mask = to_cpumask(cpu_online_bits);
EXPORT_SYMBOL(cpu_online_mask);
static DECLARE_BITMAP(cpu_present_bits, CONFIG_NR_CPUS) __read_mostly;
const struct cpumask *const cpu_present_mask = to_cpumask(cpu_present_bits);
EXPORT_SYMBOL(cpu_present_mask);
static DECLARE_BITMAP(cpu_active_bits, CONFIG_NR_CPUS) __read_mostly;
const struct cpumask *const cpu_active_mask = to_cpumask(cpu_active_bits);
EXPORT_SYMBOL(cpu_active_mask);
static DECLARE_BITMAP(cpu_possible_bits, CONFIG_NR_CPUS) __read_mostly;
const struct cpumask *const cpu_possible_mask =to_cpumask(cpu_possible_bits);
EXPORT_SYMBOL(cpu_possible_mask);
函数DECLARE_BITMAP的定义:
#define DECLARE_BITMAP(name,bits) \
unsigned long name[BITS_TO_LONGS(bits)]
也就是最后cpu_online_bit/cpu_active_bits/cpu_present_bits/cpu_possible_bit分别都是一个数组。BITS_TO_LONGS(bits)
#define BITS_PER_BYTE 8
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
#define DIV_ROUND_UP __KERNEL_DIV_ROUND_UP
#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
to_cpumask函数:
/*
进行一下类型转换,把数组转换位struct cpumask结构体类型。
*/
#define to_cpumask(bitmap) \
((struct cpumask *)(1 ? (bitmap) \
: (void *)sizeof(__check_is_bitmap(bitmap))))
static inline int __check_is_bitmap(const unsigned long *bitmap)
{
return 1;
}
其中调用的函数解析:
static inline void cpumask_set_cpu(unsigned int cpu, struct cpumask *dstp)
{
set_bit(cpumask_check(cpu), cpumask_bits(dstp));
}
#define cpumask_bits(maskp) ((maskp)->bits)
static inline unsigned int cpumask_check(unsigned int cpu)
{
#ifdef CONFIG_DEBUG_PER_CPU_MAPS
WARN_ON_ONCE(cpu >= nr_cpumask_bits);
#endif /* CONFIG_DEBUG_PER_CPU_MAPS */
return cpu;
}
/*
这是对condition条件进行判断。
具体可以看一下likely和unlikely函数的使用
这里的使用还是很有意思,linux有很多这种应用,对编译器的使用达到炉火纯青。。。。处处都考虑到了优化
*/
#define WARN_ON_ONCE(condition) ({ \
static bool __section(.data.unlikely) __warned; \
int __ret_warn_once = !!(condition); \
\
#如果__ret_warn_once为0,并且__warned为0,则设置__warned为true \
if (unlikely(__ret_warn_once)) \
if (WARN_ON(!__warned)) \
__warned = true; \
#如果__ret_warn_once为1,则回返__ret_warn_once的值
unlikely(__ret_warn_once); \
})
/*
这里就涉及到了mips中的原子操作内容,这里的支持可是指令级别的,而且有些特殊。
仔细看一下,由其是对指令ll/sc的使用。
还有这里我对函数进行了删减,只保留了有用的部分,方便分析查看。
*/
static inline void set_bit(unsigned long nr, volatile unsigned long *addr)
{
unsigned long *m = ((unsigned long *) addr) + (nr >> SZLONG_LOG);
int bit = nr & SZLONG_MASK;
unsigned long temp;
do {
__asm__ __volatile__(
" .set mips3 \n"
__WEAK_LLSC_MB
#把*m的值加载到temp中
" " __LL "%0, %1 # set_bit \n"
#根据bit的值,设置temp中对应的位
" or %0, %2 \n"
#把temp的值存储到*m中,并判断之前的操作是否是原子操作
" " __SC "%0, %1 \n"
" .set mips0 \n"
: "=&r" (temp), "+m" (*m)
: "ir" (1UL << bit));
} while (unlikely(!temp));
smp_llsc_mb(); #这就是保证指令的完成
}
/*
说一下这里的嵌入汇编。(基本格式可以看一下百度,也可以看一下我之前的记录)
输入参数:bit(准确说这个才是输入参处("ir" (1UL << bit))))
输出参数:输出参数temp,和*m
%0代表temp,%1代表 *m,%2代表bit
*/
第八个:
void __init page_address_init(void)
{
int i;
//对7个hash 表进行初始化,首先初始化其链表结点,再初始化其锁
//也就是说7个hash table的锁的初始值是一样的。
for (i = 0; i < ARRAY_SIZE(page_address_htable); i++) {
INIT_LIST_HEAD(&page_address_htable[i].lh);
spin_lock_init(&page_address_htable[i].lock);
}
}
/*
* Hash table bucket
*/
#define PA_HASH_ORDER 7
static struct page_address_slot {
struct list_head lh; /* List of page_address_maps */
spinlock_t lock; /* Protect this bucket's list */
} ____cacheline_aligned_in_smp page_address_htable[1<<PA_HASH_ORDER];
第九个:
打印内核信息:
pr_notice("%s", linux_banner);
#define pr_notice(fmt, ...) \
printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
const char linux_banner[] =
"Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@"
LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n";
#define UTS_RELEASE "3.10.0+"
#define UTS_MACHINE "mips64"
#define UTS_VERSION "#80 SMP PREEMPT Thu Oct 19 11:26:44 CST 2017"
#define LINUX_COMPILE_BY "wk"
#define LINUX_COMPILE_HOST "localhost.localdomain"
#define LINUX_COMPILER "gcc version 4.4.7 20120313(GCC)"
上一篇: linux设备驱动(二)