PREPARE THE TASK:
After the initialization and hypervisor setup, the raspvisor will execute the following code from main.c :
if (sd_init() < 0)
PANIC("sd_init() failed.");
struct raw_binary_loader_args bl_args1 = {
.load_addr = 0x0,
.entry_point = 0x0,
.sp = 0x100000,
.filename = "mini-os.bin",
};
if (create_task(raw_binary_loader, &bl_args1) < 0) {
printf("error while starting task");
return;
}
struct raw_binary_loader_args bl_args2 = {
.load_addr = 0x0,
.entry_point = 0x0,
.sp = 0x100000,
.filename = "echo.bin",
};
if (create_task(raw_binary_loader, &bl_args2) < 0) {
printf("error while starting task");
return;
}
struct raw_binary_loader_args bl_args3 = {
.load_addr = 0x0,
.entry_point = 0x0,
.sp = 0x100000,
.filename = "mini-os.bin",
};
if (create_task(raw_binary_loader, &bl_args3) < 0) {
printf("error while starting task #2");
return;
}
struct raw_binary_loader_args bl_args4 = {
.load_addr = 0x0,
.entry_point = 0x0,
.sp = 0x100000,
.filename = "echo.bin",
};
if (create_task(raw_binary_loader, &bl_args4) < 0) {
printf("error while starting task");
return;
}
struct raw_binary_loader_args bl_args5 = {
.load_addr = 0x0,
.entry_point = 0x0,
.sp = 0x100000,
.filename = "mini-os.bin",
};
if (create_task(raw_binary_loader, &bl_args5) < 0) {
printf("error while starting task");
return;
}
while (1) {
disable_irq();
schedule();
enable_irq();
}
}The first thing that is done is the SD card reader hardware initialization, that is not so interesting for the hypervisor functionality, but is necessary to make possible to load binaries stored on SD Card.
The next section of code is repeated 5 times, so we can take a look only on one of them:
struct raw_binary_loader_args bl_args1 = {
.load_addr = 0x0,
.entry_point = 0x0,
.sp = 0x100000,
.filename = "mini-os.bin",
};
if (create_task(raw_binary_loader, &bl_args1) < 0) {
printf("error while starting task");
return;
}First the struct of type raw_binary_loader_args defined on loader.h file, and called bl_args1 is initialized with the details about the binary image
The next function called is create_task where the struct bl_args1 is passed as the first argument. The second argument is the function (pointer) of the loader, called raw_binary_loader and defined at loader.c
Let’s take a look at the create_task function definition on task.c file :
int create_task(loader_func_t loader, void *arg) {
struct task_struct *p;
p = (struct task_struct *)allocate_page();
struct pt_regs *childregs = task_pt_regs(p);
if (!p)
return -1;
p->cpu_context.x19 = (unsigned long)prepare_task;
p->cpu_context.x20 = (unsigned long)loader;
p->cpu_context.x21 = (unsigned long)arg;
p->flags = 0;
p->priority = current->priority;
p->state = TASK_RUNNING;
p->counter = p->priority;
p->name = "VM";
p->board_ops = &bcm2837_board_ops;
if (HAVE_FUNC(p->board_ops, initialize))
p->board_ops->initialize(p);
prepare_initial_sysregs();
memcpy(&p->cpu_sysregs, &initial_sysregs,
sizeof(struct cpu_sysregs));
p->cpu_context.pc = (unsigned long)switch_from_kthread;
p->cpu_context.sp = (unsigned long)childregs;
int pid = nr_tasks++;
task[pid] = p;
p->pid = pid;
init_task_console(p);
return pid;
}- The first thing that is done is the creation of a pointer to a struct of type
task_struct(defined on sched.h), namedp. The memory to store thetask_structis allocated by theallocate_page(defined at mm.c) function call. - Among the fields of
task_struct, oncpu_contextentry, the registersx19-x21are filled withprepare_task,loaderandargaddresses respectively.
p->cpu_context.x19 = (unsigned long)prepare_task;
p->cpu_context.x20 = (unsigned long)loader;
p->cpu_context.x21 = (unsigned long)arg;- Now, the
task_structentry calledboard_ops, is assigned to the address ofbcm2837_board_ops:
p->board_ops = &bcm2837_board_ops;- The next section uses a macro called
HAVE_FUNC(also defined on board.h) to check if the functioninitializeonboard_opsstruct was defined and, if it was defined, the function is called.
if (HAVE_FUNC(p->board_ops, initialize))
p->board_ops->initialize(p);- Just to recall, in the next lines, following the
initializefunction call:
prepare_initial_sysregs();
memcpy(&p->cpu_sysregs, &initial_sysregs, sizeof(struct cpu_sysregs));- At the end of the function we have some operations that will modify the program counter (
cpu_context.pc), the stack pointer (cpu_context.sp).