2013年12月11日 星期三

How linux initramfs start and simulate

1. 
###after linux built
~/objs/linux-5.10.107/arch/arm64/kernel/vmlinux.lds
__initcall_start = 
.; 
KEEP(*(.initcallearly.init)) __initcall0_start = .;
KEEP(*(.initcall0.init)) KEEP(*(.initcall0s.init)) __initcall1_start = .;
KEEP(*(.initcall1.init)) KEEP(*(.initcall1s.init)) __initcall2_start = .;
KEEP(*(.initcall2.init)) KEEP(*(.initcall2s.init)) __initcall3_start = .;
KEEP(*(.initcall3.init)) KEEP(*(.initcall3s.init)) __initcall4_start = .;
KEEP(*(.initcall4.init)) KEEP(*(.initcall4s.init)) __initcall5_start = .;
KEEP(*(.initcall5.init)) KEEP(*(.initcall5s.init)) __initcallrootfs_start = .;
KEEP(*(.initcallrootfs.init)) KEEP(*(.initcallrootfss.init)) __initcall6_start = .;
KEEP(*(.initcall6.init)) KEEP(*(.initcall6s.init)) __initcall7_start = .;
KEEP(*(.initcall7.init)) KEEP(*(.initcall7s.init)) __initcall_end = .;

2.
~/linux-5.10.107/include/linux/compiler.h
#define __ADDRESSABLE(sym) \
    static void * __section(".discard.addressable") __used \
        __UNIQUE_ID(__PASTE(__addressable_,sym)) = (void *)&sym;

~/linux-5.10.107/include/linux/init.h
#define ___define_initcall(fn, id, __sec)            \
    __ADDRESSABLE(fn)                    \
    asm(".section    \"" #__sec ".init\", \"a\"    \n"    \
    "__initcall_" #fn #id ":            \n"    \
        ".long    " #fn " - .            \n"    \
        ".previous                    \n");

3.
~/linux-5.10.107/init/initramfs.c
rootfs_initcall(populate_rootfs);

4.
~/linux-5.10.107/init/main.c
static initcall_entry_t *initcall_levels[] __initdata = {
    __initcall0_start,
    __initcall1_start,
    __initcall2_start,
    __initcall3_start,
    __initcall4_start,
    __initcall5_start,
    __initcall6_start,
    __initcall7_start,
    __initcall_end,
};

int __init_or_module do_one_initcall(initcall_t fn)
{
    int count = preempt_count();
    char msgbuf[64];
    int ret;

    if (initcall_blacklisted(fn))
        return -EPERM;

    do_trace_initcall_start(fn);
    ret = fn();
    do_trace_initcall_finish(fn, ret);

    msgbuf[0] = 0;

    if (preempt_count() != count) {
        sprintf(msgbuf, "preemption imbalance ");
        preempt_count_set(count);
    }
    if (irqs_disabled()) {
        strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf));
        local_irq_enable();
    }
    WARN(msgbuf[0], "initcall %pS returned with %s\n", fn, msgbuf);

    add_latent_entropy();
    return ret;
}

static void __init do_initcall_level(int level, char *command_line)
{
    initcall_entry_t *fn;

    parse_args(initcall_level_names[level],
           command_line, __start___param,
           __stop___param - __start___param,
           level, level,
           NULL, ignore_unknown_bootoption);

    trace_initcall_level(initcall_level_names[level]);
    for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
        do_one_initcall(initcall_from_entry(fn));
}

5. full source simulate
#define INITCALL(fn) \
static initcall_t __initcall_##fn __attribute__((section(".initcalls"), used)) = fn

typedef int (*initcall_t)(void);

int l0_fn(void)    {
    printf("level0");
    return 0;
}
int l1_fn(void)    {
    printf("level1\n");
    return 0;
}
int l2_fn(void)    {
    printf("level2\n");
    return 0;
}
int l3_fn(void)    {
    printf("level3\n");
    return 0;
}
int l4_fn(void)    {
    printf("level4\n");
    return 0;
}
int l5_fn(void)    {
    printf("level5\n");
    return 0;
}
int rootfs_fn(void){
    printf("rootfs (should run in level5)\n");
    return 0;
}
int l6_fn(void)    {
    printf("level6\n");
    return 0;
}
int l7_fn(void)    {
    printf("level7\n");
    return 0;
}

/* --- level0 start --- */
asm(
".section \".initcalls\",\"aw\"\n"
".global __initcall0_start\n"
"__initcall0_start:\n"
".previous\n"
);
/* level0 entries */
INITCALL(l0_fn);

/* --- level1 start --- */
asm(
".section \".initcalls\",\"aw\"\n"
".global __initcall1_start\n"
"__initcall1_start:\n"
".previous\n"
);
/* level1 entries */
INITCALL(l1_fn);

/* --- level2 start --- */
asm(
".section \".initcalls\",\"aw\"\n"
".global __initcall2_start\n"
"__initcall2_start:\n"
".previous\n"
);
/* level2 entries */
INITCALL(l2_fn);

/* --- level3 start --- */
asm(
".section \".initcalls\",\"aw\"\n"
".global __initcall3_start\n"
"__initcall3_start:\n"
".previous\n"
);
/* level3 entries */
INITCALL(l3_fn);

/* --- level4 start --- */
asm(
".section \".initcalls\",\"aw\"\n"
".global __initcall4_start\n"
"__initcall4_start:\n"
".previous\n"
);
/* level4 entries */
INITCALL(l4_fn);

/* --- level5 start --- */
asm(
".section \".initcalls\",\"aw\"\n"
".global __initcall5_start\n"
"__initcall5_start:\n"
".previous\n"
);
/* level5 entries (put after __initcall5_start) */
INITCALL(l5_fn);

/* --- rootfs boundary (insert between level5 and level6) --- */
asm(
".section \".initcalls\",\"aw\"\n"
".global __initcallrootfs_start\n"
"__initcallrootfs_start:\n"
".previous\n"
);
/* rootfs entry (put in range of level5 ) */
INITCALL(rootfs_fn);

/* --- level6 start --- */
asm(
".section \".initcalls\",\"aw\"\n"
".global __initcall6_start\n"
"__initcall6_start:\n"
".previous\n"
);
/* level6 entries */
INITCALL(l6_fn);

/* --- level7 start --- */
asm(
".section \".initcalls\",\"aw\"\n"
".global __initcall7_start\n"
"__initcall7_start:\n"
".previous\n"
);
/* level7 entries */
INITCALL(l7_fn);

/* --- end label --- */
asm(
".section \".initcalls\",\"aw\"\n"
".global __initcall_end\n"
"__initcall_end:\n"
".previous\n"
);

/* ---------------------------
 * 3) extern labels (define in asm) and levels array
 * --------------------------- */
extern initcall_t __initcall0_start[];
extern initcall_t __initcall1_start[];
extern initcall_t __initcall2_start[];
extern initcall_t __initcall3_start[];
extern initcall_t __initcall4_start[];
extern initcall_t __initcall5_start[];
extern initcall_t __initcall6_start[];
extern initcall_t __initcall7_start[];
extern initcall_t __initcall_end[];

static initcall_t *initcall_levels[] = {
    __initcall0_start,
    __initcall1_start,
    __initcall2_start,
    __initcall3_start,
    __initcall4_start,
    __initcall5_start,
    __initcall6_start,
    __initcall7_start,
    __initcall_end,
};

/* ---------------------------
 * 4) simulate do_initcalls loop
 * --------------------------- */
int do_one_initcall(initcall_t fn)
{
    int ret = 0;
    if (fn)
        ret = fn();
    return ret;
}

/*
this for loop executes all function pointers in memory that belong to the current level,
initcall_levels[level] → pointer to the start of level level in the .initcalls section,
initcall_levels[level+1] → pointer to the start of the next level,
fn iterates over all function pointers between these two boundaries,
level 5 execute from l5_fn to rootfs_fn.
*/
static void do_initcall_level(int level)
{
    initcall_t *fn;
    printf(">>> running level %d\n", level);
    for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++) {
        do_one_initcall(*fn);
    }
}

int main() {
    for (int level = 0; level <= 7; level++)
        do_initcall_level(level);
        
    return 0;
}

6.
#gedit ./Makefile
TARGET = api_client
SRCS = main.c
CC = gcc
CFLAGS = -O0 -g

$(TARGET): $(SRCS)
    $(CC) $(CFLAGS) $^ -o $@

all: $(TARGET)

clean:
    rm -f $(TARGET) *.o

.PHONY: all clean

7.
nm -n ./api_client
0000000000416498 D __initcall5_start
0000000000416498 d _ZL16__initcall_l5_fn
00000000004164a0 D __initcallrootfs_start
00000000004164a0 d _ZL20__initcall_rootfs_fn
00000000004164a8 D __initcall6_start

ps:
How is rootfs_fn added to the end of level 5? That's not the right question.
The execution of rootfs_fn is not because it is "deliberately placed at the end of level 5".
Is it because rootfs_fn is between initcall_levels[5] and initcall_levels[6], and therefore executed.

沒有留言:

張貼留言