源码如下:
kernel/irq/irqdesc.c
int __init early_irq_init(void)
{
int i, initcnt, node = first_online_node;
struct irq_desc *desc;
init_irq_default_affinity();
/* Let arch update nr_irqs and return the nr of preallocated irqs */
// initcnt 为 16
initcnt = arch_probe_nr_irqs();
printk(KERN_INFO "NR_IRQS:%d nr_irqs:%d %d\n", NR_IRQS, nr_irqs, initcnt);
if (WARN_ON(nr_irqs > IRQ_BITMAP_BITS))
nr_irqs = IRQ_BITMAP_BITS;
if (WARN_ON(initcnt > IRQ_BITMAP_BITS))
initcnt = IRQ_BITMAP_BITS;
if (initcnt > nr_irqs)
nr_irqs = initcnt;
for (i = 0; i < initcnt; i++) {
desc = alloc_desc(i, node, NULL);
set_bit(i, allocated_irqs);
irq_insert_desc(i, desc);
}
return arch_early_irq_init();
}
先看看first_online_node,定义在include/linux/nodemask.h,为0
init_irq_default_affinity();函数如下
static void __init init_irq_default_affinity(void)
{
alloc_cpumask_var(&irq_default_affinity, GFP_NOWAIT);
cpumask_setall(irq_default_affinity);
}
这个函数大概就是分配一个bitmap,全局变量irq_default_affinity,长度为4个bit,并把它们全部置位
kernel/irq/manage.c:116:cpumask_var_t irq_default_affinity;
cpumask_var_t 的类型定义:
./include/linux/cpumask.h:652:typedef struct cpumask *cpumask_var_t;
include/linux/cpumask.h:15:typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t;
NR_CPUS 为 4
#define DECLARE_BITMAP(name,bits) \
unsigned long name[BITS_TO_LONGS(bits)]
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
#define BITS_PER_BYTE 8
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
下面看看arch_probe_nr_irqs:
int __init arch_probe_nr_irqs(void)
{
// 4.8判断这个名字
nr_irqs = machine_desc->nr_irqs ? machine_desc->nr_irqs : NR_IRQS;
// machine_desc->nr_irqs为0,NR_IRQS为16
return nr_irqs;
}
首先看看machine_desc的由来,
定义在:arch/arm/kernel/setup.c:150:const struct machine_desc *machine_desc __initdata;
只是一个指针,在哪里赋值?
setup_arch
此函数在start_kernel函数启动流程中,早于early_irq_init();和init_IRQ();
arch/arm/kernel/setup.c
void __init setup_arch(char **cmdline_p)
{
...
const struct machine_desc *mdesc;
...
// 这个值为0x83000000,即为传入设备树的物理地址
// arch/arm/kernel/head-common.S:116: .long __atags_pointer @ r6
// __atags_pointer参数这个全局变量就是r6的寄存器值;是设备树在内存中的起始地址;
// 根据传入的设备树dtb的首地址完成一些初始化操作;
mdesc = setup_machine_fdt(__atags_pointer);
if (!mdesc)
mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type);
machine_desc = mdesc;
...
}
分析函数setup_machine_fdt
/**
* setup_machine_fdt - Machine setup when an dtb was passed to the kernel
* @dt_phys: physical address of dt blob
*
* If a dtb was passed to the kernel in r2, then use it to choose the
* correct machine_desc and to setup the system.
*/
const struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
{
const struct machine_desc *mdesc, *mdesc_best = NULL;
#ifdef CONFIG_ARCH_MULTIPLATFORM
DT_MACHINE_START(GENERIC_DT, "Generic DT based system")
MACHINE_END
mdesc_best = &__mach_desc_GENERIC_DT;
#endif
// dt_phys即为传入的参数__atags_pointer,设备树的物理地址
if (!dt_phys || !early_init_dt_verify(phys_to_virt(dt_phys)))
return NULL;
// 返回的mdesc定义为arch/arm/mach-imx/mach-imx6ul.c:214:DT_MACHINE_START(IMX6UL, "Freescale i.MX6 Ultralite (Device Tree)")
mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);
if (!mdesc) {
const char *prop;
int size;
unsigned long dt_root;
early_print("\nError: unrecognized/unsupported "
"device tree compatible list:\n[ ");
dt_root = of_get_flat_dt_root();
prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
while (size > 0) {
early_print("'%s' ", prop);
size -= strlen(prop) + 1;
prop += strlen(prop) + 1;
}
early_print("]\n\n");
dump_machine_table(); /* does not return */
}
// 4-7
/* We really don't want to do this, but sometimes firmware provides buggy data */
if (mdesc->dt_fixup)
mdesc->dt_fixup();
//主要是进行早期的初始化,根据DTB配置,进行早期的初始化,以后专门分析设备树的解析在细看把
early_init_dt_scan_nodes();
/* Change machine number to match the mdesc we're using */
// nr静态初始化为~0
__machine_arch_type = mdesc->nr;
return mdesc;
}
关于 DT_MACHINE_START
#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,
#define MACHINE_END \
};
#define DT_MACHINE_START(_name, _namestr) \
static const struct machine_desc __mach_desc_##_name \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = ~0, \
.name = _namestr,
DT_MACHINE_START(GENERIC_DT, "Generic DT based system")
MACHINE_END
展开为:
static const struct machine_desc __mach_desc_GENERIC_DT
__used
__attribute__((__section__(".arch.info.init"))) = {
.nr = ~0,
.name = "Generic DT based system"
}
看of_flat_dt_match_machine
/**
* of_flat_dt_match_machine - Iterate match tables to find matching machine.
*
* @default_match: A machine specific ptr to return in case of no match.
* @get_next_compat: callback function to return next compatible match table.
*
* Iterate through machine match tables to find the best match for the machine
* compatible string in the FDT.
*/
// 第二个参数为arch_get_next_mach
const void * __init of_flat_dt_match_machine(const void *default_match,
const void * (*get_next_compat)(const char * const**))
{
const void *data = NULL;
const void *best_data = default_match;
const char *const *compat;
unsigned long dt_root;
unsigned int best_score = ~1, score = 0;
// 此函数直接返回0
dt_root = of_get_flat_dt_root();
// compat就是上面说的__mach_desc_GENERIC_DT
while ((data = get_next_compat(&compat))) {
score = of_flat_dt_match(dt_root, compat);
if (score > 0 && score < best_score) {
best_data = data;
best_score = score;
// 这个分支会走到,best_data 不是NULL
}
}
// 下面这个if不可能走到
if (!best_data) {
const char *prop;
int size;
pr_err("\n unrecognized device tree list:\n[ ");
prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
if (prop) {
while (size > 0) {
printk("'%s' ", prop);
size -= strlen(prop) + 1;
prop += strlen(prop) + 1;
}
}
printk("]\n\n");
return NULL;
}
// 此函数详见下
pr_info("Machine model: %s\n", of_flat_dt_get_machine_name());
// 最终会指向 DT_MACHINE_START(IMX6UL, "Freescale i.MX6 Ultralite (Device Tree)")
return best_data;
}
第二参数为arch_get_next_mach
static const void * __init arch_get_next_mach(const char *const **match)
{
static const struct machine_desc *mdesc = __arch_info_begin;
const struct machine_desc *m = mdesc;
if (m >= __arch_info_end)
return NULL;
mdesc++;
// dt_compat: array of device tree * 'compatible' strings
*match = m->dt_compat;
return m;
}
__arch_info_begin是啥?
arch/arm/kernel/vmlinux.lds.S中:
188 .init.arch.info : {
189 __arch_info_begin = .;
190 *(.arch.info.init)
191 __arch_info_end = .;
192 }
其实这个函数就是依次遍历内存空间中的此类型结构体变量
就是用DT_MACHINE_START
看of_flat_dt_match
/**
* of_flat_dt_match - Return true if node matches a list of compatible values
*/
int __init of_flat_dt_match(unsigned long node, const char *const *compat)
{
return of_fdt_match(initial_boot_params, node, compat);
}
initial_boot_params在early_init_dt_verify中赋值->setup_machine_fdt,传入的值其实为phys_to_virt(__atags_pointer)
__atags_pointer设备树物理地址0x83000000的来历:
再看一下bootloader的参数bootcmd的内容:
=> env print bootcmd
bootcmd=tftp 80800000 zImage; tftp 83000000 imx6ull-alientek-emmc.dtb; bootz 80800000 - 83000000
// arch/arm/kernel/head-common.S:116: .long __atags_pointer @ r6
// __atags_pointer参数这个全局变量就是r6的寄存器值;是设备树在内存中的起始地址;
看of_fdt_match:
/**
* of_fdt_match - Return true if node matches a list of compatible values
*/
这里的blob就是作为参数传入的initial_boot_params,0x83000000的虚拟地址
int of_fdt_match(const void *blob, unsigned long node,
const char *const *compat)
{
unsigned int tmp, score = 0;
if (!compat)
return 0;
while (*compat) {
tmp = of_fdt_is_compatible(blob, node, *compat);
if (tmp && (score == 0 || (tmp < score)))
score = tmp;
compat++;
}
return score;
}
看
/**
* of_fdt_is_compatible - Return true if given node from the given blob has
* compat in its compatible list
* @blob: A device tree blob
* @node: node to test
* @compat: compatible string to compare with compatible list.
*
* On match, returns a non-zero value with smaller values returned for more
* specific compatible values.
*/
这里的blob就是作为参数传入的initial_boot_params,0x83000000的虚拟地址
int of_fdt_is_compatible(const void *blob,
unsigned long node, const char *compat)
{
const char *cp;
int cplen;
unsigned long l, score = 0;
// node 为0这里是获取第一个属性值?
cp = fdt_getprop(blob, node, "compatible", &cplen);
if (cp == NULL)
return 0;
while (cplen > 0) {
score++;
if (of_compat_cmp(cp, compat, strlen(compat)) == 0)
return score;
l = strlen(cp) + 1;
cp += l;
cplen -= l;
}
return 0;
}
看fdt_getprop
fdt_getprop
const void *fdt_getprop(const void *fdt, int nodeoffset, const char *name, int *lenp)
eg: mac = fdt_getprop(gd->fdt_blob, node, “mac-address”, &len);
功能:获得节点node的某个字符串属性值。
看of_flat_dt_get_machine_name
const char * __init of_flat_dt_get_machine_name(void)
{
const char *name;
//这个函数很简单,直接返回0
unsigned long dt_root = of_get_flat_dt_root();
// 这个函数会调用fdt_getprop,获取根结点的model属性
name = of_get_flat_dt_prop(dt_root, "model", NULL);
if (!name) {
name = of_get_flat_dt_prop(dt_root, "compatible", NULL);
}
return name;
}
看 alloc_desc
分配一个 struct irq_desc
,关于这个类型定义:可以参考这篇blog,还有struct irq_chip
相关介绍