阅读:3286回复:0
booting linux for ia64 [frowarded from USTC bbs]
Booting Linux IA64...( No need for Ia64 basic yet)
--------------------- * Relative Files: $/arch/ia64/boot/ + bootloader.c + bootloader.lds $/arch/ia64/ + vmlinux.ld.S * First, Let\'s look into vmlinux.ld.S; It\'s ld script,which is used when linking target \"vmlinux\";we can get kernel image map from it; I don\'t think there is anything special here. But please note that many AT((.section) - PAGE_OFFSET); From ld manual, we know that it means the section will be loaded at result of AT((.section) - PAGE_OFFSET)), while it\'s relocatation(virtual) address still starts from PAGE_OFFSET; //Mabye here, we should ask for why? * Let\'s looking into bootloader.c; As we know, it\'s used to load kernel image(vmlinux) into memory; and transfer control to vmlinux; that\'s how kernel is really started; I would like to talk about these lines: ... for (i = 0; i < e_phnum; ++i) { //Note 1 req.len = sizeof(*elf_phdr); req.addr = (long) mem; ssc(fd, 1, (long) &req, e_phoff, SSC_READ); ssc((long) &stat, 0, 0, 0, SSC_WAIT_COMPLETION); if (stat.count != sizeof(*elf_phdr)) { cons_write(\"failed to read phdr\\n\"); return; } e_phoff += sizeof(*elf_phdr); elf_phdr = (struct elf_phdr *) mem; req.len = elf_phdr->p_filesz; req.addr = __pa(elf_phdr->p_vaddr); // Note2 ssc(fd, 1, (long) &req, elf_phdr->p_offset, SSC_READ); ssc((long) &stat, 0, 0, 0, SSC_WAIT_COMPLETION); memset((char *)__pa(elf_phdr->p_vaddr) + elf_phdr->p_filesz, 0, elf_phdr->p_memsz - elf_phdr->p_filesz); } ... Note 1 : Read ELF program header, to determine the size, address of sections Note 2 : Read section into __pa(.->p_vaddr); that\'s same as AT(.(section) -..); then set rest of section to zero; +----------------------+ | xxx | zero | +----------------------+ |-- filesz --| |------- memsz --------| and then, starting kernel now, jump to e_entry; ... e_entry = elf->e_entry; ... asm volatile (\"mov sp=%2; mov r28=%1; br.sptk.few %0\" :: \"b\"(e_entry), \"r\"(bp), \"r\"(__pa(&stack))); ... and in vmlinux.ld.S ... ENTRY(_start) OUTPUT_ARCH(ia64) ENTRY(phys_start) SECTIONS { /* Sections to be discarded */ /DISCARD/ : { *(.text.exit) *(.data.exit) *(.exitcall.exit) } v = PAGE_OFFSET; /* this symbol is here to make debugging easier... */ phys_start = _start - PAGE_OFFSET; . = KERNEL_START; ..... And things get clear now: * Kernel is loaded at KERNEL_START - PAGE_OFFSET * entry is _start \'s load address * jump to e_entry is jump to _start * +------------+ 0x0 | | | 68*4k*4k | <- (from bootloader.lds, we know that bootloader will be loaded at 0x100000) | (???) | | | +------------+ 0x00004400000 |... | |... | |_start: | |... | |... | | | +------------+ and we could find \"_start\" in arch/ia64/kernel/head.S easy; XXX I find there are very different here between different kernel verisons, but basicly, XXX they do the same things, only in different ways ;-) * What\'s more? Here, we don\'t talk about how bootloader is loaded and executed. Some guys know anything about it? share it! Booting Linux IA64 ... ---------------------- * Relative files: $/arch/ia64/ +vmlinux.lds.S $/arch/ia64/kernel/ + head.S + init_task.c $/init/ + main.c * Switch into Virutal Mode: As we known, $/arch/ia64/boot/bootloader.c, will transfer CPU control to _start in head.S (via jump); When control is transferred to _start, the bootload has already loaded us to the correct address. All that\'s left to do here is to set up the kernel\'s global pointer and jump to the kernel entry point. Kernel Image Map ( Physical addressing): +----------+ <- 0x000 | xxx | ---------- <- 0x100000 (boot loader) | | | .text | | | | .data |<- static variables \"stack\", \"memory\", \"buffer\" is located here | | +----------+ ... +----------+ <- 0x440,0000 (68*4k*4k) | | | xxx | <- kernel image | | +----------+ Before switch to virtual mode, we have to prepare some processor registers, kernel mapping, so that kernel could get to work correctly; So we have to setup mapping of kernel data and text; /* * Initialize the region register for region 7 and install a translation register * that maps the kernel\'s text and data: */ 1 rsm psr.i | psr.ic 2 mov r16=((ia64_rid(IA64_REGION_ID_KERNEL, PAGE_OFFSET) << 8) | (_PAGE_SIZE_64M << 2)) 3 ;; 4 srlz.i 5 mov r18=_PAGE_SIZE_64M<<2 6 movl r17=PAGE_OFFSET + 64*1024*1024 7 ;; 8 mov rr[r17]=r16 9 mov cr.itir=r18 10 mov cr.ifa=r17 11 mov r16=IA64_TR_KERNEL 12 movl r18=(64*1024*1024 | PAGE_KERNEL) 13 ;; 14 srlz.i 15 ;; 16 itr.i itr[r16]=r18 17 ;; 18 itr.d dtr[r16]=r18 19 ;; 20 srlz.i Here, if you want to understand what these codes do, pls see ia64 manual (vol2, ch.4) detailly; We need to know the following things: * Kernel Page Size is 64M ( It\'s big engouh,one single page contains whole kernel image) * Kernel Image is mapped starting from physical address 64M to virtual address PAGE_OFFSET+64M Now ready to switch now, and the switching process is simple; /* * Switch into virtual mode: */ 1 movl r16=(IA64_PSR_IT|IA64_PSR_IC|IA64_PSR_DT|IA64_PSR_RT|IA64_PSR_DFH|IA64_PSR_BN) 2 ;; 3 mov cr.ipsr=r16 4 movl r17=1f 5 ;; 6 mov cr.iip=r17 7 mov cr.ifs=r0 8 ;; 9 rfi 1://label ... We know that and ipsr,iip,ifs is used to restore processor state on Return From interrupt(rfi); and ipsr is used to reload psr; iip is used to restore ip; ifs is used to reload current stack frame(CFM); More details see IA64 manual; 1-3 Setup ipsr, -- with enabling IA64_PSR_IT, IA64 virtual instruction address are translated and access with rights check, and before this, instruction access use physical addressing; -- with enabling IA64_PSR_DT, IA64 virtual data address are translated and access with rights check; and before this, data access use physical addressing; -- with enabling IA64_PRS_RT, IA64 register stack are translated and access rights are checked; if disabled,the access use physical addressing; -- with enabling IA64_PRS_IC, that\'s enbling Interrupt colleciton; when interruption occurs, current state of processor is loaded in IIP, IPSR, IIM, and IFS; -- with enabling IA64_PSR_DFH, will disable high part of float-point registers sets (f32-f127); that means,we could only use lower part of float-pointer register files in kernel; this should consider for effective task switching; -- with enabling IA64_PSR_BN, GR16-32 will be accessable for bank 1; Pls see IA64 manual for more details; These bits are really seriously affect following kernel action in future; 6 Setup iip to //label ( next instruction to be executed) 7 Setup ifs to zero 9 return from interrupt, the above registers are loaded into corresponding registers; and now ... * Welcome to new world! So where we are? we are now in virtual mode now; What\'s more need to do? * Setup interrupt handler vector (IVT-based), (??) * Setup global data pointer, so that function calls & global variables could be referenced correctly From linux for ia6 kernel design document ,we should know * r13 -- contains current task pointer, (see current.h); (in user-model, as defined is software convention, r13 is reserved for thread pointer); * ar.k6 -- physical address of current task structure * ar.k7 -- physical address of page table And with these hints, let\'s come to the code; ... #ifdef CONFIG_SMP /* * Find the init_task for the currently booting CPU. At poweron, and in * UP mode, cpucount is 0. */ movl r3=cpucount ;; ld4 r3=[r3] // r3 <- smp_processor_id() movl r2=init_tasks ;; shladd r2=r3,3,r2 ;; ld8 r2=[r2] #else mov r3=0 movl r2=init_task_union ;; #endif ... (DON\'T care SMP or UP here), r3 contain current processor_id(zero if in UP-mode),and r2 contains idle process task_struct pointer; it\'s virtual address; ... // -- Note 1 cmp4.ne isAP,isBP=r3,r0 ;; // RAW on r2 extr r3=r2,0,61 // r3 == phys addr of task struct ;; // -- Note 2 // load the \"current\" pointer (r13) and ar.k6 with the current task mov r13=r2 mov IA64_KR(CURRENT)=r3 // Physical address // -- Note 3 // initialize k4 to a safe value (64-128MB is mapped by TR_KERNEL) mov IA64_KR(CURRENT_STACK)=1 // -- Note 4 /* * Reserve space at the top of the stack for \"struct pt_regs\". Kernel threads * don\'t store interesting values in that structure, but the space still needs * to be there because time-critical stuff such as the context switching can * be implemented more efficiently (for example, __switch_to() * always sets the psr.dfh bit of the task it is switching to). */ addl r12=IA64_STK_OFFSET-IA64_PT_REGS_SIZE-16,r2 addl r2=IA64_RBS_OFFSET,r2 // initialize the RSE mov ar.rsc=0 // place RSE in enforced lazy mode ;; loadrs // clear the dirty partition ;; mov ar.bspstore=r2 // establish the new RSE stack ;; mov ar.rsc=0x3 // place RSE in eager mode ... Note 1: and here is something interesting: $/arch/ia64/kernel/init_task.c ... /* * Initial task structure. * * We need to make sure that this is page aligned due to the way * process stacks are handled. This is done by having a special * \"init_task\" linker map entry.. */ union task_union init_task_union __attribute__((section(\"init_task\"))) = { INIT_TASK(init_task_union.task) }; $/arch/ia64/vmlinux.ld.S ... /* The initial task and kernel stack */ init_task : AT(ADDR(init_task) - PAGE_OFFSET) { *(init_task) } ... So , \"extra r3=r2, 0 ,61 \" simply got rid of high 3-bit RID; and r3 now contains the physical address of init_task\'s task_struct pointer; Note 2: It seems the comments make things clear engouh ;-) But pls note the difference from r13 and ar.k6: -- r13 contains virtual address; and ar.k6 contains physical address. (Question: why two register is used here?) Note 3: (Question: 1?) Note 4: You may found this map in the source code, but let\'s make it ourself; r12 -- by software convention, contains thread stack and we must remember here , the address used here are all physical, not virtual. (init_task) +--------------+ <- physical address (low) | task_struct | +--------------+ <- BSP ( down) | /// | | /// | +--------------+ <- stack pointer(up) | pt_regs | +--------------+ (high) * Then _start call start_kernel() to transfer control to machine-independant part of kernel, which is located in $/init/main.c; ... br.call.sptk.few rp=start_kernel ... and it should never return; if there is something wrong, kernel will print out halt_msg(\"halting kernel\"), and result in endless loop; ... .ret2: addl r2=@ltoff(halt_msg),gp //Note 1 ;; ld8 out0=[r2] br.call.sptk.few b0=console_print self: br.sptk.few self // endless loop ... Note 1: I want to point out something used frequently in IA64 assembly code; you may find more informations in IA64 manual; Here i just present your some ideas: gp --> global data pointer, actually, it\'s another name of R1; DON\'t ASK WHY, it\'s just convention compiler should obey; @ltoff(expr) --> The current instruction that instructs the linker to create a linkage table entry for expr, and calculates the gp-relative offset to the new linkage table entry.add long immediate instructions. so here, this instruction is to load halt_msg address into r2; XXX if you don\'t understand what it means here, it means you need to read some artciles XXX about linker ;-) And here we won\'t get involved into start_kernel(), cause there are a lot of documents have describe it in detail; *Summary Ok, now kernel is really started; And we get to know one interface between machine-dependant and machine-independant: start_kernel; Before start_kernel(), the machine-dependant part does: * Load Kernel image into indicated location (bootloader) * Setup address translation (mm) * Setup interrupts and faults handling (bottom half of kernel service) * \"Create\" IDLE process avaible (process manangement, without it init process could not be created ) Any way, I never get chance of porting linux to new target, just something in mind, it\'s in-complete, pseudo,and buggy ;-) *What\'s next? I will dive into IA64 address model and interrupt handling with codes step by step; I hope i could draw sketch between machine-dependant(arch) part and machine-independant part via analyzing; *Questions -- As i know, one single 64M page is mapped for kernel image, memory access in this range is ok; but how about other areas? in fact, i don\'t find the related code that maps the kernel space to physical memory(like IA32). so some kind guys would tell me? -- why kernel image is loaded at 68M, not 64M? this make a 4M hole! -- ※ 来源: 中国科大BBS站 [bbs.ustc.edu.cn] |
|
|