GVKun编程网logo

linux kernel mini2440 start.S head-common.S 部分注释(linux no rule to make target needed by)

29

本文的目的是介绍linuxkernelmini2440start.Shead-common.S部分注释的详细情况,特别关注linuxnoruletomaketargetneededby的相关信息。我们

本文的目的是介绍linux kernel mini2440 start.S head-common.S 部分注释的详细情况,特别关注linux no rule to make target needed by的相关信息。我们将通过专业的研究、有关数据的分析等多种方式,为您呈现一个全面的了解linux kernel mini2440 start.S head-common.S 部分注释的机会,同时也不会遗漏关于arm Linux 系统启动之 ----start_kernel 函数、How to configure the Linux kernel/Loadable module support、jz2440-linux3.4.2-kernel移植【学习笔记】【原创】、kernel2.6.32.2 mini2440 led 驱动 之 中断(去抖动)处理的知识。

本文目录一览:

linux kernel mini2440 start.S head-common.S 部分注释(linux no rule to make target needed by)

linux kernel mini2440 start.S head-common.S 部分注释(linux no rule to make target needed by)

内核版本:2.6.32.2(mini2440光盘源码)

github地址:https://github.com/guanglun/mini2440_uboot_linux (for_len分支 https://github.com/guanglun/mini2440_uboot_linux/tree/for_learn)

  1 /*
  2  *  linux/arch/arm/kernel/head.S
  3  *
  4  *  Copyright (C) 1994-2002 Russell King
  5  *  Copyright (c) 2003 ARM Limited
  6  *  All Rights Reserved
  7  *
  8  * This program is free software; you can redistribute it and/or modify
  9  * it under the terms of the GNU General Public License version 2 as
 10  * published by the Free Software Foundation.
 11  *
 12  *  Kernel startup code for all 32-bit CPUs
 13  */
 14 #include <linux/linkage.h>
 15 #include <linux/init.h>
 16 
 17 #include <asm/assembler.h>
 18 #include <asm/domain.h>
 19 #include <asm/ptrace.h>
 20 #include <asm/asm-offsets.h>
 21 #include <asm/memory.h>
 22 #include <asm/thread_info.h>
 23 #include <asm/system.h>
 24 
 25 #if (PHYS_OFFSET & 0x001fffff)
 26 #error "PHYS_OFFSET must be at an even 2MiB boundary!"
 27 #endif
 28 
 29 /*
 30 PAGE_OFFSET            0xC0000000     页表偏移地址
 31 PHYS_OFFSET            0x30000000     物理内存偏移地址
 32 TEXT_OFFSET            0x00008000
 33 KERNEL_RAM_VADDR    0xC0008000     内核虚拟映射地址
 34 KERNEL_RAM_PADDR      0x30008000     内核物理(RAM)地址
 35 */
 36 
 37 #define KERNEL_RAM_VADDR    (PAGE_OFFSET + TEXT_OFFSET)
 38 #define KERNEL_RAM_PADDR    (PHYS_OFFSET + TEXT_OFFSET)
 39 
 40 
 41 /*
 42  * swapper_pg_dir is the virtual address of the initial page table.
 43  * We place the page tables 16K below KERNEL_RAM_VADDR.  Therefore, we must
 44  * make sure that KERNEL_RAM_VADDR is correctly set.  Currently, we expect
 45  * the least significant 16 bits to be 0x8000, but we could probably
 46  * relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000.
 47  */
 48 
 49  /*
 50  swapper_pg_dir是初始化页表的虚拟地址
 51  我们把页表放在KERNEL_RAM_VADDR下面,因此我们必须保证KERNEL_RAM_VADDR设置得正确。
 52  当前我们要求最低有效的16位数值位0x8000,
 53  但我们可能放宽此限制为KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000
 54  */
 55 
 56 #if (KERNEL_RAM_VADDR & 0xffff) != 0x8000
 57 #error KERNEL_RAM_VADDR must start at 0xXXXX8000
 58 #endif
 59 
 60 /*
 61 
 62     .equ 虽然数据段主要用于定义变量数据,但是也可以在这里声明静态数据符号。
 63     .equ 命令用于把常量值设置为可以在文本段中使用的符号
 64 */
 65     .globl    swapper_pg_dir
 66     .equ    swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000
 67 //定义rgtbl 宏 功能是将KERNEL_RAM_PADDR - 0x4000复制给rd寄存器
 68     .macro    pgtbl, rd
 69     ldr    \rd, =(KERNEL_RAM_PADDR - 0x4000)
 70     .endm
 71 
 72 /*CONFIG_XIP_KERNEL未设置
 73 *#define KERNEL_START    KERNEL_RAM_VADDR
 74 *#define KERNEL_END        _end
 75 */
 76 
 77 //XIP片上执行
 78 #ifdef CONFIG_XIP_KERNEL
 79 #define KERNEL_START    XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)
 80 #define KERNEL_END    _edata_loc
 81 #else
 82 #define KERNEL_START    KERNEL_RAM_VADDR
 83 #define KERNEL_END    _end
 84 #endif
 85 
 86 /*
 87  * Kernel startup entry point.
 88  * ---------------------------
 89  *
 90  * This is normally called from the decompressor code.  The requirements
 91  * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
 92 
 93  * 0xc0008000, you call this at __pa(0xc0008000).
 94  *
 95  * See linux/arch/arm/tools/mach-types for the complete list of machine
 96  * numbers for r1.
 97  *
 98  * We''re trying to keep crap to a minimum; DO NOT add any machine specific
 99  * crap here - that''s what the boot loader (or in extreme, well justified
100  * circumstances, zImage) is for.
101  */
102  /**
103  内核运行入口
104  MCR指令将ARM处理器的寄存器中的数据传送到协处理器的寄存器中。如果协处理器不能成功地执行该操作,将产生未定义的指令异常中断。
105  MRC指令将协处理器的寄存器中数值传送到ARM处理器的寄存器中。如果协处理器不能成功地执行该操作,将产生未定义的指令异常中断。
106  **/
107     .section ".text.head", "ax"
108 ENTRY(stext)
109     setmode    PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode
110                         @ and irqs disabled
111     mrc    p15, 0, r9, c0, c0        @ get processor id            //获取处理器ID            r9 = cpuid
112     bl    __lookup_processor_type        @ r5=procinfo r9=cpuid     //处理器类型是否支持
113 
114     beq    __error_p            @ yes, error ''p''
115     bl    __lookup_machine_type        @ r5=machinfo            //机器(板子)类型是否支持
116     movs    r8, r5                @ invalid machine (r5=0)?                            r8 = machinfo
117     beq    __error_a            @ yes, error ''a''
118     bl    __vet_atags                //确定r2 atags指针的有效性   无参数???
119 
120     //r8 = machinfo 
121     //r9 = cpuid 
122     //r10 = procinfo
123     bl    __create_page_tables
124 
125     /*
126      * The following calls CPU specific code in a position independent
127      * manner.  See arch/arm/mm/proc-*.S for details.  r10 = base of
128      * xxx_proc_info structure selected by __lookup_machine_type
129      * above.  On return, the CPU will be ready for the MMU to be
130      * turned on, and r0 will hold the CPU control register value.
131      */
132     ldr    r13, __switch_data        @ address to jump to after      r13 = __mmap_switched(虚拟地址)
133                         @ mmu has been enabled
134     adr    lr, BSYM(__enable_mmu)        @ return (PIC) address
135  ARM(    add    pc, r10, #PROCINFO_INITFUNC    )
136  THUMB(    add    r12, r10, #PROCINFO_INITFUNC    )
137  THUMB(    mov    pc, r12                )
138 ENDPROC(stext)
139 
140 #if defined(CONFIG_SMP)
141 ENTRY(secondary_startup)
142     /*
143      * Common entry point for secondary CPUs.
144      *
145      * Ensure that we''re in SVC mode, and IRQs are disabled.  Lookup
146      * the processor type - there is no need to check the machine type
147      * as it has already been validated by the primary processor.
148      */
149     setmode    PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9
150     mrc    p15, 0, r9, c0, c0        @ get processor id
151     bl    __lookup_processor_type
152     movs    r10, r5                @ invalid processor?
153     moveq    r0, #''p''            @ yes, error ''p''
154     beq    __error
155 
156     /*
157      * Use the page tables supplied from  __cpu_up.
158      */
159     adr    r4, __secondary_data
160     ldmia    r4, {r5, r7, r12}        @ address to jump to after
161     sub    r4, r4, r5            @ mmu has been enabled
162     ldr    r4, [r7, r4]            @ get secondary_data.pgdir
163     adr    lr, BSYM(__enable_mmu)        @ return address
164     mov    r13, r12            @ __secondary_switched address
165  ARM(    add    pc, r10, #PROCINFO_INITFUNC    ) @ initialise processor
166                           @ (return control reg)
167  THUMB(    add    r12, r10, #PROCINFO_INITFUNC    )
168  THUMB(    mov    pc, r12                )
169 ENDPROC(secondary_startup)
170 
171     /*
172      * r6  = &secondary_data
173      */
174 ENTRY(__secondary_switched)
175     ldr    sp, [r7, #4]            @ get secondary_data.stack
176     mov    fp, #0
177     b    secondary_start_kernel
178 ENDPROC(__secondary_switched)
179 
180     .type    __secondary_data, %object
181 __secondary_data:
182     .long    .
183     .long    secondary_data
184     .long    __secondary_switched
185 #endif /* defined(CONFIG_SMP) */
186 
187 
188 
189 /*
190  * Setup common bits before finally enabling the MMU.  Essentially
191  * this is just loading the page table pointer and domain access
192  * registers.
193  */
194 __enable_mmu:
195 #ifdef CONFIG_ALIGNMENT_TRAP
196     orr    r0, r0, #CR_A            //执行
197 #else
198     bic    r0, r0, #CR_A
199 #endif
200 #ifdef CONFIG_CPU_DCACHE_DISABLE
201     bic    r0, r0, #CR_C            //未执行
202 #endif
203 #ifdef CONFIG_CPU_BPREDICT_DISABLE
204     bic    r0, r0, #CR_Z            //未执行
205 #endif
206 #ifdef CONFIG_CPU_ICACHE_DISABLE
207     bic    r0, r0, #CR_I            //未执行
208 #endif
209     mov    r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
210               domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
211               domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
212               domain_val(DOMAIN_IO, DOMAIN_CLIENT))    
213 
214 //MRC:协处理器寄存器到ARM处理器寄存器的数据传送指令(读出协处理器寄存器)。
215 //MCR:ARM处理器寄存器到协处理器寄存器的数据传送指令(写入协处理器寄存器)。
216 
217     //c3 DOMAIN ACCESS CONTROL REGISTER
218     mcr    p15, 0, r5, c3, c0, 0        @ load domain access register
219     //r4 = 30004000 放进协处理器c2
220     mcr    p15, 0, r4, c2, c0, 0        @ load page table pointer
221 
222     b    __turn_mmu_on
223 ENDPROC(__enable_mmu)
224 
225 /*
226  * Enable the MMU.  This completely changes the structure of the visible
227  * memory space.  You will not be able to trace execution through this.
228  * If you have an enquiry about this, *please* check the linux-arm-kernel
229  * mailing list archives BEFORE sending another post to the list.
230  *
231  *  r0  = cp#15 control register
232  *  r13 = *virtual* address to jump to upon completion
233  *
234  * other registers depend on the function called upon completion
235  */
236     .align    5
237 __turn_mmu_on:
238     mov    r0, r0
239     mcr    p15, 0, r0, c1, c0, 0        @ write control reg
240     mrc    p15, 0, r3, c0, c0, 0        @ read id reg
241     mov    r3, r3
242     mov    r3, r13
243     mov    pc, r3
244 ENDPROC(__turn_mmu_on)
245 
246 
247 /*
248  * Setup the initial page tables.  We only setup the barest
249  * amount which are required to get the kernel running, which
250  * generally means mapping in the kernel code.
251  *
252  * r8  = machinfo
253  * r9  = cpuid
254  * r10 = procinfo
255  *
256  * Returns:
257  *  r0, r3, r6, r7 corrupted
258  *  r4 = physical page table address
259  */
260  //创建页表
261 __create_page_tables:
262 /* 
263 *宏定义 *.macro pgtbl, rd *ldr \rd, =(KERNEL_RAM_PADDR - 0x4000) *.endm * 
264 *分析:内存为 4G = 4*1024 MB;需要4096个表单表示,每一页表单为4bytes;因此需要16K内存,即0x4000; 
265 *根据 =(KERNEL_RAM_PADDR - 0x4000)得,页表存放于-内核在内存中的物理地址之前。 
266 */
267 
268 //定义rgtbl 宏 功能是将KERNEL_RAM_PADDR - 0x4000复制给rd寄存器
269     pgtbl    r4                @ page table address       //r4 = 30004000
270 
271     /*
272      * Clear the 16K level 1 swapper page table
273      */
274      /* * 按16个bytes一次,将页表清空 */
275     mov    r0, r4        //r0 = 30004000
276     mov    r3, #0        //r3 = 0
277     add    r6, r0, #0x4000 //r6 = 30008000
278     //将30004000-30008000 的空间置0
279 1:    str    r3, [r0], #4
280     str    r3, [r0], #4
281     str    r3, [r0], #4
282     str    r3, [r0], #4
283     teq    r0, r6
284     bne    1b
285 
286 /* *r10 = proc_info_list类型结构体的基地址 *PROCINFO_MM_MMUFLAGS 8 /* offsetof(struct proc_info_list, __cpu_mm_mmu_flags) @ */
287     ldr    r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags   r7 = 00000C1E __cpu_mm_mmu_flags
288 
289     /*
290      * Create identity mapping for first MB of kernel to
291      * cater for the MMU enable.  This identity mapping
292      * will be removed by paging_init().  We use our current program
293      * counter to determine corresponding section base address.
294      */
295 
296      /*
297      
298      为第一个MB内核创建标识映射,以满足MMU启用。 此标识映射将通过paging_init()删除。 我们使用当前的程序计数器来确定相应的部分基址。
299 
300      */
301 
302 /* 
303 *下面代码建立kernel对应的section页表项。 * 
304 *1. 通过PC值的高12位(右移20位),得到kernel的section,并存储在r6中。 
305 *2. 获取32bit的页表表单值 
306 *3. 将页表表单值存放在页表内存区中。 * 
307 *注意点: *a. lsr 20 因为虚拟地址分区中,后20位为相对地址,前12位为段地址 
308 *b. lsl 2 因为每一个页表项为4字节,所以需要左移2位 
309 */
310 
311 /*页表将4GB的地址空间分成若干个1MB的段(section),因此页表包含4096个页表项(section entry)。
312 每个页表项是32bits(4 bytes),因而页表占用4096*4=16k的内存空间。下面的代码是将这16k的页表清0。
313 */
314 
315     mov    r6, pc   //r6= 3000xxxx 通过pc值的高12位(右移20位),得到kernel的section,并存储到r6中.因为当前是通过运行时地址得到的kernel的section,因而是物理地址. 
316     mov    r6, r6, lsr #20            @ start of kernel section  r6 = 00000300
317     orr    r3, r7, r6, lsl #20        @ flags + kernel base   r3 = 30000C1E = r7 | (r6 << 20); flags + kernel base,得到页表中需要设置的值. 
318     str    r3, [r4, r6, lsl #2]        @ identity mapping  //设置页表: mem[r4 + r6 * 4] = r3   r4 = 30004000
319                                                         //         mem(30004000 + 300 << 2) = 30000C1E
320                                                         //         mem(30004000 + 300 * 4)  = 30000C1E
321                                                         //            mem(30004C00) = 30000C1E
322                                                         //这里,因为页表的每一项是32 bits(4 bytes),所以要乘以4(<<2). 
323 
324     /*
325      * Now setup the pagetables for our kernel direct
326      * mapped region.
327      */
328 /* 
329 * 下面的 add r0, r4, #(KERNEL_START & 0xff000000) >> 18 涉及到一个立即数的概念: 
330 * 关于arm汇编立即数,可以参考下面网站:http://blog.csdn.net/a99778800/article/details/6759825 * 
331 * 下面这段代码就是存储kernel物理地址。建立页表,虚拟地址和物理地址之间建立连接。 
332 * 即:将内核中所有的物理地址(1M为单位)都存放到了页表中,与虚拟地址一一对应。 */     
333 
334 //KERNEL_START = 0xC0008000
335 //这样分开写是由于arm的立即数只能是8位表示。
336     add    r0, r4,  #(KERNEL_START & 0xff000000) >> 18     //r0 = 30007000 R4 =30004000
337     str    r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!
338     ldr    r6, =(KERNEL_END - 1)
339     add    r0, r0, #4
340     add    r6, r4, r6, lsr #18
341 1:    cmp    r0, r6
342     add    r3, r3, #1 << 20
343     strls    r3, [r0], #4
344     bls    1b
345 
346 /*
347 运行到此 段表在内存中如下:
348 
349 J-Link>mem32 30007000 10
350 30007000 = 30000C1E 30100C1E 30200C1E 30300C1E 
351 30007010 = 30400C1E 00000000 00000000 00000000 
352 30007020 = 00000000 00000000 00000000 00000000 
353 30007030 = 00000000 00000000 00000000 00000000 
354 J-Link>h
355 PC: (R15) = 300080D8, CPSR = 200000D3 (SVC mode, ARM FIQ dis. IRQ dis.)
356 Current:
357      R0 =30007014, R1 =000007CF, R2 =00000000, R3 =30500C1E
358      R4 =30004000, R5 =00000000, R6 =30007013, R7 =00000C1E
359      R8 =3001EF70, R9 =41129200, R10=3001EF3C, R11=00000020, R12=306EFD84
360      R13=3049D990, R14=30008028, SPSR=00000010
361 USR: R8 =3001EF70, R9 =41129200, R10=3001EF3C, R11=00000020, R12=306EFD84
362      R13=00000000, R14=00000000
363 FIQ: R8 =00000000, R9 =00000000, R10=00000000, R11=00000000, R12=00000000
364      R13=00000000, R14=00000000, SPSR=00000010
365 IRQ: R13=00000000, R14=00000000, SPSR=00000010
366 SVC: R13=3049D990, R14=30008028, SPSR=00000010
367 ABT: R13=00000000, R14=00000000, SPSR=00000010
368 UND: R13=00000000, R14=00000000, SPSR=00000010
369 
370 
371 */
372 
373 
374 /* 
375 * XIP介绍: 
376 * XIP是指 (EXECUTE IN PLACE) 是指直接从存放代码的位置上启动运行。 
377 * 非XIP方式是指在运行之前需对代码进行重定位。该类型的内核以非压缩方式存放在Flash中,启动时由Bootloader加载到内存后运行。 * 
378 * 如果是XIP技术的内核,上面的映射只能映射内核代码和只读数据部分 
379 * 这里我们再映射一些RAM来作为.data and .bss空间。 
380 */
381 
382 #ifdef CONFIG_XIP_KERNEL XIP 未定义
383     /*
384      * Map some ram to cover our .data and .bss areas.
385      */
386     orr    r3, r7, #(KERNEL_RAM_PADDR & 0xff000000)
387     .if    (KERNEL_RAM_PADDR & 0x00f00000)
388     orr    r3, r3, #(KERNEL_RAM_PADDR & 0x00f00000)
389     .endif
390     //这样分开写是由于arm的立即数只能是8位表示。
391     add    r0, r4,  #(KERNEL_RAM_VADDR & 0xff000000) >> 18
392     str    r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!
393     ldr    r6, =(_end - 1)
394     add    r0, r0, #4
395     add    r6, r4, r6, lsr #18
396 1:    cmp    r0, r6
397     add    r3, r3, #1 << 20
398     strls    r3, [r0], #4
399     bls    1b
400 #endif
401 
402     /*
403      * Then map first 1MB of ram in case it contains our boot params.
404      * 然后映射第一个1MB的RAM,以防它包含我们的引导参数。
405      */
406 /* 
407 * 下面的代码用来设置RAM中大小为1M虚拟地址的页表。之所以要设置这个页表项的原因是该区域存储着boot params。 
408 * 因此需要为它建立map,这样开启MMU后就可以访问 
409 */     
410 
411 //这样分开写是由于arm的立即数只能是8位表示。
412 
413 /*
414 *下面的代码用来设置RAM中起始地址为0x30000000、大小为1M虚拟地址的页表,之所以要设置这个页表项的原因是该区域起始地址为0x30000100存
415 *储着boot params。因此需要为它建立map,这样开启MMU后就可以访问这些参数了。
416 */
417     add    r0, r4, #PAGE_OFFSET >> 18
418     orr    r6, r7, #(PHYS_OFFSET & 0xff000000)
419     .if    (PHYS_OFFSET & 0x00f00000)
420     orr    r6, r6, #(PHYS_OFFSET & 0x00f00000)
421     .endif
422     str    r6, [r0]
423 
424 
425 #ifdef CONFIG_DEBUG_LL
426     ldr    r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
427     /*
428      * Map in IO space for serial debugging.
429      * This allows debug messages to be output
430      * via a serial console before paging_init.
431      */
432     ldr    r3, [r8, #MACHINFO_PGOFFIO]
433     add    r0, r4, r3
434     rsb    r3, r3, #0x4000            @ PTRS_PER_PGD*sizeof(long)
435     cmp    r3, #0x0800            @ limit to 512MB
436     movhi    r3, #0x0800
437     add    r6, r0, r3
438     ldr    r3, [r8, #MACHINFO_PHYSIO]
439     orr    r3, r3, r7
440 1:    str    r3, [r0], #4
441     add    r3, r3, #1 << 20
442     teq    r0, r6
443     bne    1b
444 #if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)
445     /*
446      * If we''re using the NetWinder or CATS, we also need to map
447      * in the 16550-type serial port for the debug messages
448      */
449     add    r0, r4, #0xff000000 >> 18
450     orr    r3, r7, #0x7c000000
451     str    r3, [r0]
452 #endif
453 #ifdef CONFIG_ARCH_RPC
454     /*
455      * Map in screen at 0x02000000 & SCREEN2_BASE
456      * Similar reasons here - for debug.  This is
457      * only for Acorn RiscPC architectures.
458      */
459     add    r0, r4, #0x02000000 >> 18
460     orr    r3, r7, #0x02000000
461     str    r3, [r0]
462     add    r0, r4, #0xd8000000 >> 18
463     str    r3, [r0]
464 #endif
465 #endif
466     mov    pc, lr
467 ENDPROC(__create_page_tables)
468     .ltorg
469 
470 #include "head-common.S"

 

  1 /*
  2  *  linux/arch/arm/kernel/head-common.S
  3  *
  4  *  Copyright (C) 1994-2002 Russell King
  5  *  Copyright (c) 2003 ARM Limited
  6  *  All Rights Reserved
  7  *
  8  * This program is free software; you can redistribute it and/or modify
  9  * it under the terms of the GNU General Public License version 2 as
 10  * published by the Free Software Foundation.
 11  *
 12  */
 13 
 14 #define ATAG_CORE 0x54410001
 15 #define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)
 16 #define ATAG_CORE_SIZE_EMPTY ((2*4) >> 2)
 17 
 18     .align    2
 19     .type    __switch_data, %object
 20 __switch_data:
 21     .long    __mmap_switched
 22     .long    __data_loc            @ r4
 23     .long    _data                @ r5
 24     .long    __bss_start            @ r6
 25     .long    _end                @ r7
 26     .long    processor_id            @ r4
 27     .long    __machine_arch_type        @ r5
 28     .long    __atags_pointer            @ r6
 29     .long    cr_alignment            @ r7
 30     .long    init_thread_union + THREAD_START_SP @ sp
 31 
 32 /*
 33  * The following fragment of code is executed with the MMU on in MMU mode,
 34  * and uses absolute addresses; this is not position independent.
 35  *
 36  *  r0  = cp#15 control register
 37  *  r1  = machine ID
 38  *  r2  = atags pointer
 39  *  r9  = processor ID
 40  */
 41  /*
 42 R0~R15 和 r0~r15 (16 个通用寄存器);
 43 a1~a4(参数,结果或临时寄存器,同 R0~R3);
 44 v1~v8(变量寄存器,同 R4~R11);
 45 SB 和 sb(静态基址,同 R9);
 46 SL 和 sl(堆栈限制,同 R10);
 47 FP 和 fp(帧指针);
 48 IP 和 ip(过程调用中间临时寄存器,同 R12);
 49 SP 和 sp(堆栈指针,同 R13);
 50 LR 和 lr(链接寄存器,同 R14);
 51 PC 和 pc(程序计数器,同 R15).
 52  */
 53 __mmap_switched:
 54     adr    r3, __switch_data + 4
 55 
 56 //R4 =C0460000, R5 =C0460000, R6 =C049D8E0, R7 =C04D1D38
 57     ldmia    r3!, {r4, r5, r6, r7} 
 58     cmp    r4, r5                @ Copy data segment if needed
 59     //以下ne都不会执行
 60 1:    cmpne    r5, r6
 61     ldrne    fp, [r4], #4
 62     strne    fp, [r5], #4
 63     bne    1b
 64 
 65     mov    fp, #0                @ Clear BSS (and zero fp)
 66 1:    cmp    r6, r7
 67     strcc    fp, [r6],#4
 68     bcc    1b
 69 
 70  ARM(    ldmia    r3, {r4, r5, r6, r7, sp})
 71  THUMB(    ldmia    r3, {r4, r5, r6, r7}    )
 72  THUMB(    ldr    sp, [r3, #16]        )
 73     str    r9, [r4]            @ Save processor ID
 74     str    r1, [r5]            @ Save machine type
 75     str    r2, [r6]            @ Save atags pointer
 76     bic    r4, r0, #CR_A            @ Clear ''A'' bit
 77     stmia    r7, {r0, r4}            @ Save control register values
 78     b    start_kernel
 79 ENDPROC(__mmap_switched)
 80 
 81 /*
 82  * Exception handling.  Something went wrong and we can''t proceed.  We
 83  * ought to tell the user, but since we don''t have any guarantee that
 84  * we''re even running on the right architecture, we do virtually nothing.
 85  *
 86  * If CONFIG_DEBUG_LL is set we try to print out something about the error
 87  * and hope for the best (useful if bootloader fails to pass a proper
 88  * machine ID for example).
 89  */
 90 __error_p:
 91 #ifdef CONFIG_DEBUG_LL
 92     adr    r0, str_p1
 93     bl    printascii
 94     mov    r0, r9
 95     bl    printhex8
 96     adr    r0, str_p2
 97     bl    printascii
 98     b    __error
 99 str_p1:    .asciz    "\nError: unrecognized/unsupported processor variant (0x"
100 str_p2:    .asciz    ").\n"
101     .align
102 #endif
103 ENDPROC(__error_p)
104 
105 __error_a:
106 #ifdef CONFIG_DEBUG_LL
107     mov    r4, r1                @ preserve machine ID
108     adr    r0, str_a1
109     bl    printascii
110     mov    r0, r4
111     bl    printhex8
112     adr    r0, str_a2
113     bl    printascii
114     adr    r3, 4f
115     ldmia    r3, {r4, r5, r6}        @ get machine desc list
116     sub    r4, r3, r4            @ get offset between virt&phys
117     add    r5, r5, r4            @ convert virt addresses to
118     add    r6, r6, r4            @ physical address space
119 1:    ldr    r0, [r5, #MACHINFO_TYPE]    @ get machine type
120     bl    printhex8
121     mov    r0, #''\t''
122     bl    printch
123     ldr     r0, [r5, #MACHINFO_NAME]    @ get machine name
124     add    r0, r0, r4
125     bl    printascii
126     mov    r0, #''\n''
127     bl    printch
128     add    r5, r5, #SIZEOF_MACHINE_DESC    @ next machine_desc
129     cmp    r5, r6
130     blo    1b
131     adr    r0, str_a3
132     bl    printascii
133     b    __error
134 ENDPROC(__error_a)
135 
136 str_a1:    .asciz    "\nError: unrecognized/unsupported machine ID (r1 = 0x"
137 str_a2:    .asciz    ").\n\nAvailable machine support:\n\nID (hex)\tNAME\n"
138 str_a3:    .asciz    "\nPlease check your kernel config and/or bootloader.\n"
139     .align
140 #endif
141 
142 __error:
143 #ifdef CONFIG_ARCH_RPC
144 /*
145  * Turn the screen red on a error - RiscPC only.
146  */
147     mov    r0, #0x02000000
148     mov    r3, #0x11
149     orr    r3, r3, r3, lsl #8
150     orr    r3, r3, r3, lsl #16
151     str    r3, [r0], #4
152     str    r3, [r0], #4
153     str    r3, [r0], #4
154     str    r3, [r0], #4
155 #endif
156 1:    mov    r0, r0
157     b    1b     //1b b指back 1为标号
158 ENDPROC(__error)
159 
160 
161 /*
162  * Read processor ID register (CP#15, CR0), and look up in the linker-built
163  * supported processor list.  Note that we can''t use the absolute addresses
164  * for the __proc_info lists since we aren''t running with the MMU on
165  * (and therefore, we are not in the correct address space).  We have to
166  * calculate the offset.
167  *
168  *    r9 = cpuid
169  * Returns:
170  *    r3, r4, r6 corrupted
171  *    r5 = proc_info pointer in physical address space 物理内存中的proc_info指针
172  *    r9 = cpuid (preserved)
173  */
174  //ADR : 小范围的地址读取伪指令.ADR 指令将基于 PC 相对偏移的地址值读取到寄存器中.
175  //LDMIA 和 STMIA 批量加载/存储指令可以实现在一组寄存器和一块连续的内存单元之间传输数据
176 __lookup_processor_type:
177     adr    r3, 3f    //3f f指forward 3为标号                
178     ldmia    r3, {r5 - r7} 
179 /*    
180     r5 __proc_info_begin     地址
181     r6 __proc_info_end        地址
182     r7 .                    当前地址
183 
184 
185     R0 =00000000, R1 =000007CF, R2 =016F2818, R3 =300081B8
186     R4 =30008000, R5 =C001EF3C, R6 =C001EF70, R7 =C00081C0
187     R8 =016F2818, R9 =41129200, R10=00000004, R11=00000020, R12=306EFD84
188     R13=3049D990, R14=3000800C, SPSR=00000010
189 
190 */
191 
192     add    r3, r3, #8
193     sub    r3, r3, r7            @ get offset between virt&phys  R3 = 70000000 = 300081C0 - C00081C0
194     add    r5, r5, r3            @ convert virt addresses to
195     add    r6, r6, r3            @ physical address space
196 1:    ldmia    r5, {r3, r4}            @ value, mask
197     and    r4, r4, r9            @ mask wanted bits
198     teq    r3, r4
199     beq    2f                    //ID正确,跳转回去
200     add    r5, r5, #PROC_INFO_SZ        @ sizeof(proc_info_list) ID不正确的话,寻找下一个list对比
201     cmp    r5, r6                //比较是否小于__proc_info_end的地址,如果小于则说明还有list可以比较,否则就已经没有了
202     blo    1b
203     mov    r5, #0                @ unknown processor  返回r5 = 0表示没有对应的处理器
204 2:    mov    pc, lr
205 ENDPROC(__lookup_processor_type)
206 
207 /*
208  * This provides a C-API version of the above function.
209  */
210 ENTRY(lookup_processor_type)
211     stmfd    sp!, {r4 - r7, r9, lr}
212     mov    r9, r0
213     bl    __lookup_processor_type
214     mov    r0, r5
215     ldmfd    sp!, {r4 - r7, r9, pc}
216 ENDPROC(lookup_processor_type)
217 
218 /*
219  * Look in <asm/procinfo.h> and arch/arm/kernel/arch.[ch] for
220  * more information about the __proc_info and __arch_info structures.
221  */
222 
223 
224 //proc_info和arch_info位置
225     .align    2
226 3:    .long    __proc_info_begin
227     .long    __proc_info_end
228 4:    .long    .
229     .long    __arch_info_begin
230     .long    __arch_info_end
231 
232 /*
233  * Lookup machine architecture in the linker-build list of architectures.
234  * Note that we can''t use the absolute addresses for the __arch_info
235  * lists since we aren''t running with the MMU on (and therefore, we are
236  * not in the correct address space).  We have to calculate the offset.
237  *
238  *  r1 = machine architecture number
239  * Returns:
240  *  r3, r4, r6 corrupted
241  *  r5 = mach_info pointer in physical address space
242  */
243 __lookup_machine_type:
244     adr    r3, 4b       //将 .long    . 的物理地址加载至r3寄存器 R3 =300081C0
245     ldmia    r3, {r4, r5, r6} //R4 =C00081C0, R5 =C001EF70, R6 =C001EFA4,
246 
247     //将r5  r6的__arch_info_begin和__arch_info_end地址由虚拟地址转换成物理地址
248     sub    r3, r3, r4            @ get offset between virt&phys
249     add    r5, r5, r3            @ convert virt addresses to
250     add    r6, r6, r3            @ physical address space        
251     
252 1:    ldr    r3, [r5, #MACHINFO_TYPE]    @ get machine type
253     teq    r3, r1                @ matches loader number?
254     beq    2f                @ found
255     add    r5, r5, #SIZEOF_MACHINE_DESC    @ next machine_desc
256     cmp    r5, r6
257     blo    1b
258     mov    r5, #0                @ unknown machine
259 2:    mov    pc, lr
260 ENDPROC(__lookup_machine_type)
261 
262 /*
263  * This provides a C-API version of the above function.
264  */
265 ENTRY(lookup_machine_type)
266     stmfd    sp!, {r4 - r6, lr}
267     mov    r1, r0
268     bl    __lookup_machine_type
269     mov    r0, r5
270     ldmfd    sp!, {r4 - r6, pc}
271 ENDPROC(lookup_machine_type)
272 
273 /* Determine validity of the r2 atags pointer.  The heuristic requires
274  * that the pointer be aligned, in the first 16k of physical RAM and
275  * that the ATAG_CORE marker is first and present.  Future revisions
276  * of this function may be more lenient with the physical address and
277  * may also be able to move the ATAGS block if necessary.
278  *
279  * r8  = machinfo
280  *
281  * Returns:
282  *  r2 either valid atags pointer, or zero
283  *  r5, r6 corrupted
284  */
285 __vet_atags:
286     tst    r2, #0x3            @ aligned?
287     bne    1f
288 
289     ldr    r5, [r2, #0]            @ is first tag ATAG_CORE?
290     cmp    r5, #ATAG_CORE_SIZE
291     cmpne    r5, #ATAG_CORE_SIZE_EMPTY
292     bne    1f
293     ldr    r5, [r2, #4]
294     ldr    r6, =ATAG_CORE
295     cmp    r5, r6
296     bne    1f
297 
298     mov    pc, lr                @ atag pointer is ok
299 
300 1:    mov    r2, #0
301     mov    pc, lr
302 ENDPROC(__vet_atags)

 

arm Linux 系统启动之 ----start_kernel 函数

arm Linux 系统启动之 ----start_kernel 函数

head-common.S
---具体做了哪些动作
---跳转到init/main.c 
---b start_kernel
//关于start_kernel的强文深入理解linux内核,第八章
main.c
asmlinkage void __init start_kernel(void)
{
char * command_line;
extern struct kernel_param __start___param[], __stop___param[];
//来设置smp process id,当然目前看到的代码里面这里是空的
smp_setup_processor_id();
/*
* Need to run as early as possible, to initialize the
* lockdep hash:
*/
//lockdep是linux内核的一个调试模块,用来检查内核互斥机制尤其是自旋锁潜在的死锁问题。
//自旋锁由于是查询方式等待,不释放处理器,比一般的互斥机制更容易死锁,
//故引入lockdep检查以下几种情况可能的死锁(lockdep将有专门的文章详细介绍,在此只是简单列举):
//
//·同一个进程递归地加锁同一把锁;
//
//·一把锁既在中断(或中断下半部)使能的情况下执行过加锁操作,
// 又在中断(或中断下半部)里执行过加锁操作。这样该锁有可能在锁定时由于中断发生又试图在同一处理器上加锁;
//
//·加锁后导致依赖图产生成闭环,这是典型的死锁现象。
lockdep_init();
debug_objects_early_init();
/*
* Set up the the initial canary ASAP:
*/
//初始化stack_canary栈3
//stack_canary的是带防止栈溢出攻击保护的堆栈。
//  当user space的程序通过int 0x80进入内核空间的时候,CPU自动完成一次堆栈切换, 
//从user space的stack切换到kernel space的stack。
//   在这个进程exit之前所发生的所有系统调用所使用的kernel stack都是同一个。
//kernel stack的大小一般为4096/8192,
//内核堆栈示意图帮助大家理解:
//
//   内存低址                                                              内存高址
//                      |                 |<-----------------------------esp|   
//   +-----------------------------------4096-------------------------------+
//   |        72        |     4           |       x < 4016    |     4       |
//   +------------------+-----------------+---------------------------------+
//   |thread_info  |    | STACK_END_MAGIC |   var/call chain  |stack_canary |
//   +------------------+-----------------+---------------------------------+
//   |     28      | 44 |                 |                                 |
//                 V    |                                                   |
//             restart_block                                                V
//
//esp+0x0                                                                   +0x40
//    +---------------------------------------------------------------------------+
//    |ebx|ecx|edx|esi|edi|ebp|eax|ds|es|fs|gs|orig_eax|eip|cs|eflags|oldesp|oldss|
//    +---------------------------------------------------------------------------+
//    |             kernel完成                          |         cpu自动完成       |
//http://hi.baidu.com/wzt85/blog/item/112a37132f6116c2f6039e44.html
boot_init_stack_canary(); 
//	cgroup: 它的全称为control group.即一组进程的行为控制.
//	比如,我们限制进程/bin/sh的CPU使用为20%.我们就可以建一个cpu占用为20%的cgroup.
//	然后将/bin/sh进程添加到这个cgroup中.当然,一个cgroup可以有多个进程.
//http://blogold.chinaunix.net/u1/51562/showart_1736813.html
cgroup_init_early();
//更新kernel中的所有的立即数值,但是包括哪些需要再看?
core_imv_update();
//关闭当前CUP中断
local_irq_disable();
//修改标记early_boot_irqs_enabled;
//通过一个静态全局变量 early_boot_irqs_enabled来帮助我们调试代码,
//通过这个标记可以帮助我们知道是否在”early bootup code”,也可以通过这个标志警告是有无效的终端打开
early_boot_irqs_off();
//每一个中断都有一个IRQ描述符(struct irq_desc)来进行描述。
//这个函数的主要作用是设置所有的 IRQ描述符(struct irq_desc)的锁是统一的锁,
//还是每一个IRQ描述符(struct irq_desc)都有一个小锁。
early_init_irq_lock_class();
/*
 * Interrupts are still disabled. Do necessary setups, then
 * enable them
 */
// 大内核锁(BKL--Big Kernel Lock)
//大内核锁本质上也是自旋锁,但是它又不同于自旋锁,自旋锁是不可以递归获得锁的,因为那样会导致死锁。
//但大内核锁可以递归获得锁。大内核锁用于保护整个内核,而自旋锁用于保护非常特定的某一共享资源。
//进程保持大内核锁时可以发生调度,具体实现是:
//在执行schedule时,schedule将检查进程是否拥有大内核锁,如果有,它将被释放,以致于其它的进程能够获得该锁,
//而当轮到该进程运行时,再让它重新获得大内核锁。注意在保持自旋锁期间是不运行发生调度的。
//需要特别指出,整个内核只有一个大内核锁,其实不难理解,内核只有一个,而大内核锁是保护整个内核的,当然有且只有一个就足够了。
//还需要特别指出的是,大内核锁是历史遗留,内核中用的非常少,一般保持该锁的时间较长,因此不提倡使用它。
//从2.6.11内核起,大内核锁可以通过配置内核使其变得可抢占(自旋锁是不可抢占的),这时它实质上是一个互斥锁,使用信号量实现。
//大内核锁的API包括:
//
//void lock_kernel(void);
//
//该函数用于得到大内核锁。它可以递归调用而不会导致死锁。
//
//void unlock_kernel(void);
//
//该函数用于释放大内核锁。当然必须与lock_kernel配对使用,调用了多少次lock_kernel,就需要调用多少次unlock_kernel。
//大内核锁的API使用非常简单,按照以下方式使用就可以了:
//lock_kernel(); //对被保护的共享资源的访问 … unlock_kernel();
//http://blog.csdn.net/universus/archive/2010/05/25/5623971.aspx
lock_kernel();
//初始化time ticket,时钟
tick_init();
//函数 tick_init() 很简单,调用 clockevents_register_notifier 函数向 clockevents_chain 通知链注册元素:
// tick_notifier。这个元素的回调函数指明了当时钟事件设备信息发生变化(例如新加入一个时钟事件设备等等)时,
//应该执行的操作,该回调函数为 tick_notify 
//http://blogold.chinaunix.net/u3/97642/showart_2050200.html
boot_cpu_init();
//初始化页地址,当然对于arm这里是个空函数
//http://book.chinaunix.net/special/ebook/PrenticeHall/PrenticeHallPTRTheLinuxKernelPrimer/0131181637/ch08lev1sec5.html
page_address_init();
printk(KERN_NOTICE "%s", linux_banner);
//系结构相关的内核初始化过程
//http://www.cublog.cn/u3/94690/showart_2238008.html
setup_arch(&command_line);
//初始化内存管理
mm_init_owner(&init_mm, &init_task);
//处理启动命令,这里就是设置的cmd_line
setup_command_line(command_line);
//这个在定义了SMP的时候有作用,现在这里为空函数;对于smp的使用,后面在看。。。
setup_nr_cpu_ids();
//如果没有定义CONFIG_SMP宏,则这个函数为空函数。
//如果定义了CONFIG_SMP宏,则这个setup_per_cpu_areas()函数给每个CPU分配内存,
//并拷贝.data.percpu段的数据。为系统中的每个CPU的per_cpu变量申请空间。
setup_per_cpu_areas();
//定义在include/asm-x86/smp.h。
//如果是SMP环境,则设置boot CPU的一些数据。在引导过程中使用的CPU称为boot CPU
smp_prepare_boot_cpu();	/* arch-specific boot-cpu hooks */
//设置node 和 zone 数据结构
//内存管理的讲解:http://blog.chinaunix.net/space.php?uid=361890&do=blog&cuid=2146541
build_all_zonelists(NULL);
//初始化page allocation相关结构
page_alloc_init();
printk(KERN_NOTICE "Kernel command line: %s/n", boot_command_line);
//解析内核参数
//对内核参数的解析:http://hi.baidu.com/yuhuntero/blog/item/654a7411e45ce519b8127ba9.html
parse_early_param();
parse_args("Booting kernel", static_command_line, __start___param,
  __stop___param - __start___param,
  &unknown_bootoption);
/*
* These use large bootmem allocations and must precede
* kmem_cache_init()
*/
//初始化hash表,以便于从进程的PID获得对应的进程描述指针,按照实际的物理内存初始化pid hash表
//这里涉及到进程管理http://blog.csdn.net/satanwxd/archive/2010/03/27/5422053.aspx
pidhash_init();
//初始化VFS的两个重要数据结构dcache和inode的缓存。
//http://blog.csdn.net/yunsongice/archive/2011/02/01/6171324.aspx
vfs_caches_init_early();
//把编译期间,kbuild设置的异常表,也就是__start___ex_table和__stop___ex_table之中的所有元素进行排序
sort_main_extable();
//初始化中断向量表
//http://blog.csdn.net/yunsongice/archive/2011/02/01/6171325.aspx
trap_init();
//memory map初始化
//http://blog.csdn.net/huyugv_830913/archive/2010/09/15/5886970.aspx
mm_init();
/*
* Set up the scheduler prior starting any interrupts (such as the
* timer interrupt). Full topology setup happens at smp_init()
* time - but meanwhile we still have a functioning scheduler.
*/
//核心进程调度器初始化,调度器的初始化的优先级要高于任何中断的建立,
//并且初始化进程0,即idle进程,但是并没有设置idle进程的NEED_RESCHED标志,
//所以还会继续完成内核初始化剩下的事情。
//这里仅仅为进程调度程序的执行做准备。
//它所做的具体工作是调用init_bh函数(kernel/softirq.c)把timer,tqueue,immediate三个人物队列加入下半部分的数组
sched_init();
/*
* Disable preemption - early bootup scheduling is extremely
* fragile until we cpu_idle() for the first time.
*/
//抢占计数器加1 
preempt_disable();
//检查中断是否打开
if (!irqs_disabled()) {
printk(KERN_WARNING "start_kernel(): bug: interrupts were "
"enabled *very* early, fixing it/n");
local_irq_disable();
}
//Read-Copy-Update的初始化
//RCU机制是Linux2.6之后提供的一种数据一致性访问的机制,
//从RCU(read-copy-update)的名称上看,我们就能对他的实现机制有一个大概的了解,
//在修改数据的时候,首先需要读取数据,然后生成一个副本,对副本进行修改,
//修改完成之后再将老数据update成新的数据,此所谓RCU。
//http://blog.ednchina.com/tiloog/193361/message.aspx
//http://blogold.chinaunix.net/u1/51562/showart_1341707.html
rcu_init();
//定义在lib/radix-tree.c。
//Linux使用radix树来管理位于文件系统缓冲区中的磁盘块,
//radix树是trie树的一种
//http://blog.csdn.net/walkland/archive/2009/03/19/4006121.aspx
radix_tree_init();
/* init some links before init_ISA_irqs() */
//early_irq_init 则对数组中每个成员结构进行初始化, 
//例如, 初始每个中断源的中断号.其他的函数基本为空. 
early_irq_init();
//初始化IRQ中断和终端描述符。
//初始化系统中支持的最大可能的中断描述结构struct irqdesc变量数组irq_desc[NR_IRQS],
//把每个结构变量irq_desc[n]都初始化为预先定义好的坏中断描述结构变量bad_irq_desc,
//并初始化该中断的链表表头成员结构变量pend
init_IRQ();
//prio-tree是一棵查找树,管理的是什么?
//http://blog.csdn.net/dog250/archive/2010/06/28/5700317.aspx
prio_tree_init();
//初始化定时器Timer相关的数据结构
//http://www.ibm.com/developerworks/cn/linux/l-cn-clocks/index.html
init_timers();
//对高精度时钟进行初始化
hrtimers_init();
//软中断初始化
//http://blogold.chinaunix.net/u1/51562/showart_494363.html
softirq_init();
//初始化时钟源
timekeeping_init();
//初始化系统时间,
//检查系统定时器描述结构struct sys_timer全局变量system_timer是否为空,
//如果为空将其指向dummy_gettimeoffset()函数。
//http://www.ibm.com/developerworks/cn/linux/l-cn-clocks/index.html
time_init();
//profile只是内核的一个调试性能的工具,
//这个可以通过menuconfig中的Instrumentation Support->profile打开。
//http://www.linuxdiyf.com/bbs//thread-71446-1-1.html
profile_init();
if (!irqs_disabled())
printk(KERN_CRIT "start_kernel(): bug: interrupts were "
"enabled early/n");
//与开始的early_boot_irqs_off相对应
early_boot_irqs_on();
//与local_irq_disbale相对应,开中断
local_irq_enable();
/* Interrupts are enabled now so all GFP allocations are safe. */
gfp_allowed_mask = __GFP_BITS_MASK;
//memory cache的初始化
//http://my.chinaunix.net/space.php?uid=7588746&do=blog&id=153184
kmem_cache_init_late();
/*
* HACK ALERT! This is early. We''re enabling the console before
* we''ve done PCI setups etc, and console_init() must be aware of
* this. But we do want output early, in case something goes wrong.
*/
//初始化控制台以显示printk的内容,在此之前调用的printk,只是把数据存到缓冲区里,
//只有在这个函数调用后,才会在控制台打印出内容
//该函数执行后可调用printk()函数将log_buf中符合打印级别要求的系统信息打印到控制台上。
console_init();
if (panic_later)
panic(panic_later, panic_param);
//如果定义了CONFIG_LOCKDEP宏,那么就打印锁依赖信息,否则什么也不做
lockdep_info();
/*
* Need to run this when irqs are enabled, because it wants
* to self-test [hard/soft]-irqs on/off lock inversion bugs
* too:
*/
//如果定义CONFIG_DEBUG_LOCKING_API_SELFTESTS宏
//则locking_selftest()是一个空函数,否则执行锁自测
locking_selftest();
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && !initrd_below_start_ok &&
   page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) {
printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
   "disabling it./n",
   page_to_pfn(virt_to_page((void *)initrd_start)),
   min_low_pfn);
initrd_start = 0;
}
#endif
//页面初始化,可以参考上面的cgroup机制
page_cgroup_init();
//页面分配debug启用
enable_debug_pagealloc();
//此处函数为空
kmemtrace_init();
//memory lead侦测初始化,如何侦测???
kmemleak_init();
//
//Called after the kmem_caches are functional to setup a dedicated
//cache pool, which has the SLAB_DEBUG_OBJECTS flag set. This flag
//prevents that the debug code is called on kmem_cache_free() for the
//debug tracker objects to avoid recursive calls.
//在kmem_caches之后表示建立一个高速缓冲池,建立起SLAB_DEBUG_OBJECTS标志。???
debug_objects_mem_init();
//idr在linux内核中指的就是整数ID管理机制,
//从本质上来说,这就是一种将整数ID号和特定指针关联在一起的机制
//idr机制适用在那些需要把某个整数和特定指针关联在一起的地方。
//http://blogold.chinaunix.net/u3/93255/showart_2524027.html
idr_init_cache();
//是否是对SMP的支持,单核是否需要??这个要分析
setup_per_cpu_pageset();
//NUMA (Non Uniform Memory Access) policy 
//具体是什么不懂
numa_policy_init();
if (late_time_init)
late_time_init();
//初始化调度时钟
sched_clock_init();
//calibrate_delay()函数可以计算出cpu在一秒钟内执行了多少次一个极短的循环,
//计算出来的值经过处理后得到BogoMIPS 值,
//Bogo是Bogus(伪)的意思,MIPS是millions of instructions per second(百万条指令每秒)的缩写。
//这样我们就知道了其实这个函数是linux内核中一个cpu性能测试函数。
//http://blogold.chinaunix.net/u2/86768/showart_2196664.html
calibrate_delay();
//PID是process id的缩写
//http://blog.csdn.net/satanwxd/archive/2010/03/27/5422053.aspx
pidmap_init();
//来自mm/rmap.c
//分配一个anon_vma_cachep作为anon_vma的slab缓存。
//这个技术是PFRA(页框回收算法)技术中的组成部分。
//这个技术为定位而生——快速的定位指向同一页框的所有页表项。
anon_vma_init();
#ifdef CONFIG_X86
if (efi_enabled)
efi_enter_virtual_mode();
#endif
//创建thread_info缓存
thread_info_cache_init();
//申请了一个slab来存放credentials??????如何理解?
cred_init();
//根据物理内存大小计算允许创建进程的数量
//http://www.jollen.org/blog/2006/11/jollen_linux_3_fork_init.html
fork_init(totalram_pages);
//给进程的各种资源管理结构分配了相应的对象缓存区
//http://www.shangshuwu.cn/index.php/Linux内核的进程创建
proc_caches_init();
//创建 buffer_head SLAB 缓存
buffer_init();
//初始化key的management stuff
key_init();
//关于系统安全的初始化,主要是访问控制
//http://blog.csdn.net/nhczp/archive/2008/04/29/2341194.aspx
security_init();
//与debug kernel相关
dbg_late_init();
//调用kmem_cache_create()函数来为VFS创建各种SLAB分配器缓存
//包括:names_cachep、filp_cachep、dquot_cachep和bh_cachep等四个SLAB分配器缓存
vfs_caches_init(totalram_pages);
//创建信号队列
signals_init();
/* rootfs populating might need page-writeback */
//回写相关的初始化
//http://blog.csdn.net/yangp01/archive/2010/04/06/5454822.aspx
page_writeback_init();
#ifdef CONFIG_PROC_FS
proc_root_init();
#endif
//它将剩余的subsys初始化.然后将init_css_set添加进哈希数组css_set_table[ ]中.
//在上面的代码中css_set_hash()是css_set_table的哈希函数.
//它是css_set->subsys为哈希键值,到css_set_table[ ]中找到对应项.然后调用hlist_add_head()将init_css_set添加到冲突项中.
//然后,注册了cgroup文件系统.这个文件系统也是我们在用户空间使用cgroup时必须挂载的.
//最后,在proc的根目录下创建了一个名为cgroups的文件.用来从用户空间观察cgroup的状态.
//http://blogold.chinaunix.net/u1/51562/showart_1736813.html
cgroup_init();
//http://blogold.chinaunix.net/u1/51562/showart_1777937.html
cpuset_init();
////进程状态初始化,实际上就是分配了一个存储线程状态的高速缓存
taskstats_init_early();
delayacct_init();
//此处为一空函数
imv_init_complete();
//测试CPU的各种缺陷,记录检测到的缺陷,以便于内核的其他部分以后可以使用他们工作。
check_bugs();
//电源相关的初始化
//http://blogold.chinaunix.net/u/548/showart.php?id=377952
acpi_early_init(); /* before LAPIC and SMP init */
//
sfi_init_late();
ftrace_init();
/* Do the rest non-__init''ed, we''re now alive */
//创建1号进程,详细分析之
rest_init();
}


How to configure the Linux kernel/Loadable module support

How to configure the Linux kernel/Loadable module support

Loadable module support

  • Option: Enable loadable module support
    • Kernel Versions: 2.6.15.6 ...
    • variable name: MODULES
    • (on/off)

      Kernel modules are small pieces of compiled code which can be inserted in the running kernel, rather than being permanently built into the kernel. You use the "modprobe" tool to add (and sometimes remove) them. If you say Y here, many parts of the kernel can be built as modules (by answering M instead of Y where indicated): this is most useful for infrequently used options which are not required for booting. For more information, see the man pages for modprobe, lsmod, modinfo, insmod and rmmod.

      If you say Y here, you will need to run "make modules_install" to put the modules under /lib/modules/ where modprobe can find them (you may need to be root to do this).

      If unsure, say Y.

 

  • Option: Module unloading
    • Kernel Versions: 2.6.15.6 ...
    • variable name: MODULE_UNLOAD
    • (on/off)
    • depends on MODULES

      Without this option you will not be able to unload any modules (note that some modules may not be unloadable anyway), which makes your kernel slightly smaller and simpler. If unsure, say Y.

 

  • Option: Forced module unloading
    • Kernel Versions: 2.6.15.6 ...
    • variable name: MODULE_FORCE_UNLOAD
    • (on/off)
    • depends on MODULE_UNLOAD && EXPERIMENTAL

      This option allows you to force a module to unload, even if the kernel believes it is unsafe: the kernel will remove the module without waiting for anyone to stop using it (using the -f option to rmmod). This is mainly for kernel developers and desperate users. If unsure, say N.

 

  • Option:
    • Kernel Versions: 2.6.15.6 ...
    • variable name: OBSOLETE_MODPARM
    • (on/off)
    • default y
    • depends on MODULES

      You need this option to use module parameters on modules which have not been converted to the new module parameter system yet. If unsure, say Y.

 

  • Option: Module versioning support (EXPERIMENTAL)
    • Kernel Versions: 2.6.15.6 ...
    • variable name: MODVERSIONS
    • (on/off)
    • depends on MODULES && EXPERIMENTAL

      Usually, you have to use modules compiled with your kernel. Saying Y here makes it sometimes possible to use modules compiled for different kernels, by adding enough information to the modules to (hopefully) spot any changes which would make them incompatible with the kernel you are running. If unsure, say N.

 

  • Option: Source checksum for all modules
    • Kernel Versions: 2.6.15.6 ...
    • variable name: MODULE_SRCVERSION_ALL
    • (on/off)
    • depends on MODULES

      Modules which contain a MODULE_VERSION get an extra "srcversion" field inserted into their modinfo section, which contains a sum of the source files which made it. This helps maintainers see exactly which source was used to build a module (since others sometimes change the module source without updating the version). With this option, such a "srcversion" field will be created for all modules. If unsure, say N.

 

  • Option: Automatic kernel module loading
    • Kernel Versions: 2.6.15.6 ...
    • variable name: KMOD
    • (on/off)
    • depends on MODULES

      Normally when you have selected some parts of the kernel to be created as kernel modules, you must load them (using the modprobe command) before you can use them. If you say Y here, some parts of the kernel will be able to load modules automatically: when a part of the kernel needs a module, it runs modprobe with the appropriate arguments, thereby loading the module if it is available. If unsure, say Y.

 

  • Option:
    • Kernel Versions: 2.6.15.6 ...
    • variable name: STOP_MACHINE
    • (on/off)
    • default y
    • depends on (SMP && MODULE_UNLOAD) || HOTPLUG_CPU

      Need stop_machine() primitive.

 

jz2440-linux3.4.2-kernel移植【学习笔记】【原创】

jz2440-linux3.4.2-kernel移植【学习笔记】【原创】

平台:jz2440

作者:庄泽彬(欢迎转载,请注明作者)

说明:韦东山二期视频学习笔记

交叉编译工具:arm-linux-gcc (GCC)4.3.2

linux:linu3.4.2

PC环境:ubuntu18.04

一、kernel的编译和烧录:

下载linux-3.4.2版本进行移植,下载链接:https://mirrors.edge.kernel.org/pub/linux/kernel/v3.x/

修改顶层Makefile,设置为arm架构,以及设置交叉工具链

 1 zhuang@zhuang:~/project/3-jz2440/systems/linux-3.4.2$ git diff .
 2 diff --git a/Makefile b/Makefile
 3 index 901a9557..9cc1639a 100644
 4 --- a/Makefile
 5 +++ b/Makefile
 6 @@ -192,8 +192,8 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
 7  # Default value for CROSS_COMPILE is not to prefix executables
 8  # Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile
 9  export KBUILD_BUILDHOST := $(SUBARCH)
10 -ARCH           ?= $(SUBARCH)
11 -CROSS_COMPILE  ?= $(CONFIG_CROSS_COMPILE:"%"=%)
12 +ARCH           ?= arm
13 +CROSS_COMPILE  ?= arm-linux-
14  
15  # Architecture as present in compile.h
16  UTS_MACHINE    := $(ARCH)

编译:

1 make s3c2410_defconfig
2 make uImage -j4

 

哎呀,竟然报了下面这个错误:

应该是高版本的make导致这个错误出现的,按照错误的log修改文件。

修改如下:

 1 diff --git a/kernel/timeconst.pl b/kernel/timeconst.pl
 2 index eb51d76e..04612394 100644
 3 --- a/kernel/timeconst.pl
 4 +++ b/kernel/timeconst.pl
 5 @@ -370,7 +370,7 @@ if ($hz eq ''--can'') {
 6         }
 7  
 8         @val = @{$canned_values{$hz}};
 9 -       if (!defined(@val)) {
10 +       if (!@val) {
11                 @val = compute_values($hz);
12         }
13         output($hz, @val);

 生成对应的uImage :

上述编译出来的内核烧录到板子加载内核却出现乱码,出现乱码,有可能是我们的机器ID没有设置好,导致调用的初始化错误导致的。:

 

 二 、内核的启动

uboot启动内核主要的操作如下,从nandflash里把内核读入内存,设置TAG参数(内存的起始地址大小,命令行参数等等),R1存放机器ID,R2存放参数的存放地址,内核在启动的时候会解析TAG参数,根据uboot传递过来的机器ID,判断是否能够支持该机器,从而调用对应板子的初始化函数.

uboot设置机器ID的代码主要流程如下:

Smdk2410.c (board\samsung\smdk2440) 
board_init
  gd->bd->bi_arch_number = MACH_TYPE_SMDK2410; //设置机器ID的默认值

Bootm.c (arch\arm\lib)

boot_jump_linux

  unsigned long machid = gd->bd->bi_arch_number;

  s = getenv("machid");         //可根据环境变量设置机器ID

我在cmdline随便设置一个machid,看看内核支持那些板子

set machid 33333

启动过程打印的log,根据log可以看出有匹配对应的开发板子的一个过程,最后由于匹配失败,卡在这里。

OK
Using machid 0x33333 from environment

Starting kernel ...

Uncompressing Linux... done, booting the kernel.

Error: unrecognized/unsupported machine ID (r1 = 0x00033333).

Available machine support:

ID (hex)        NAME
00000400        AML_M5900
0000014b        Simtec-BAST
0000015b        IPAQ-H1940
0000039f        Acer-N35
00000290        Acer-N30
000002a8        Nex Vision - Otom 1.1
00000454        QT2410
000000c1        SMDK2410
000005b4        TCT_HAMMER
000001db        Thorcom-VR1000
000005d2        JIVE
000003fe        SMDK2413
000003f1        SMDK2412
00000377        S3C2413
00000474        VSTMS
00000695        SMDK2416
000002de        Simtec-Anubis
00000707        AT2440EVB
000007cf        MINI2440
000002a9        NexVision - Nexcoder 2440
0000034a        Simtec-OSIRIS
00000250        IPAQ-RX3715
0000016a        SMDK2440
00000518        GTA02
000003b8        HP iPAQ RX1950
0000043c        SMDK2443

2.1 支持mini2440开发板的machid 7cf

设置cmdline波特率为115200

1 set bootargs console=ttySAC0,115200 root=/dev/mtdblock3

nfs 32000000 192.168.1.100:/work/nfs_root/uImage //烧录内核命令

 设置machid为mini2440的开发板7cf,启动,串口不会有乱码.

2.2支持smdk2440开发板的machid 16a

设置machid为smdk2440开发板16a,串口却出现乱码:

我们来看看内核smd2440开发板初始化代码,是不是跟jz2440有差异:

Mach-smdk2440.c (arch\arm\mach-s3c24xx)    
  MACHINE_START(S3C2440, "SMDK2440")

    /* Maintainer: Ben Dooks <ben-linux@fluff.org> */
     .atag_offset = 0x100,

     .init_irq = s3c24xx_init_irq,
     .map_io = smdk2440_map_io,   //这个函数的初始化化有点问题,
     .init_machine = smdk2440_machine_init,
     .timer = &s3c24xx_timer,
     .restart = s3c244x_restart,
     MACHINE_END

 

static void __init smdk2440_map_io(void)
{
s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));
s3c24xx_init_clocks(16934400);  //jz2440开发板是12M的晶振,使用使用smdk2440的machid,这里要修改为12000000
s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));
}

代码修改如下,支持smdk2440开发板,串口输出不会有乱码:

zhuang@zhuang:~/project/3-jz2440/systems/linux-3.4.2$ git diff .
diff --git a/arch/arm/mach-s3c24xx/mach-smdk2440.c b/arch/arm/mach-s3c24xx/mach-smdk2440.c
index 83a1036d..b92c2bd5 100644
--- a/arch/arm/mach-s3c24xx/mach-smdk2440.c
+++ b/arch/arm/mach-s3c24xx/mach-smdk2440.c
@@ -162,7 +162,7 @@ static struct platform_device *smdk2440_devices[] __initdata = {
 static void __init smdk2440_map_io(void)
 {
        s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc));
-       s3c24xx_init_clocks(16934400);
+       s3c24xx_init_clocks(12000000);   //jz2440开发板是12M的晶振,使用使用smdk2440的machid,这里要修改为12000000
        s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs));
 }
 

设置为smdk2440开发板的machid 16a,启动系统串口没有输出乱码见下图:

三、修改内核分区 

 内核将nandflash分区划分如下:

1 0x00000000-0x00040000 : "bootloader"
2 0x00040000-0x00060000 : "params"
3 0x00060000-0x00260000 : "kernel"
4 0x00260000-0x10000000 : "root"

 代码修改如下:

1 diff --git a/arch/arm/mach-s3c24xx/common-smdk.c b/arch/arm/mach-s3c24xx/common-smdk.c
 2 index 084604be..f7dce9b9 100644
 3 --- a/arch/arm/mach-s3c24xx/common-smdk.c
 4 +++ b/arch/arm/mach-s3c24xx/common-smdk.c
 5 @@ -110,43 +110,23 @@ static struct platform_device smdk_led7 = {
 6  
 7  static struct mtd_partition smdk_default_nand_part[] = { 8 [0] = { 9 - .name = "Boot Agent", 10 - .size = SZ_16K, 11 + .name = "bootloader", 12 + .size = SZ_256K, 13 .offset = 0, 14  }, 15 [1] = { 16 - .name = "S3C2410 flash partition 1", 17 - .offset = 0, 18 - .size = SZ_2M, 19 + .name = "params", 20 + .offset = MTDPART_OFS_APPEND, 21 + .size = SZ_128K, 22  }, 23 [2] = { 24 - .name = "S3C2410 flash partition 2", 25 - .offset = SZ_4M, 26 - .size = SZ_4M, 27 - }, 28 - [3] = { 29 - .name = "S3C2410 flash partition 3", 30 - .offset = SZ_8M, 31 + .name = "kernel", 32 + .offset = MTDPART_OFS_APPEND, 33 .size = SZ_2M, 34  }, 35 - [4] = { 36 - .name = "S3C2410 flash partition 4", 37 - .offset = SZ_1M * 10, 38 - .size = SZ_4M, 39 - }, 40 - [5] = { 41 - .name = "S3C2410 flash partition 5", 42 - .offset = SZ_1M * 14, 43 - .size = SZ_1M * 10, 44 - }, 45 - [6] = { 46 - .name = "S3C2410 flash partition 6", 47 - .offset = SZ_1M * 24, 48 - .size = SZ_1M * 24, 49 - }, 50 - [7] = { 51 - .name = "S3C2410 flash partition 7", 52 - .offset = SZ_1M * 48, 53 + [3] = { 54 + .name = "rootfs", 55 + .offset = MTDPART_OFS_APPEND, 56 .size = MTDPART_SIZ_FULL, 57  } 58  }; 59 zhuang@zhuang:~/project/3-jz2440/systems/linux-3.4.2$ 

 内核启动将会把nandflash划分为上述四个分区

四、制作新的文件系统

3.1编译busybox1.20.0

解压busybox 

tar -xvf busybox-1.20.0.tar.bz2

设置交叉工具链make menuconfig

编译

1 make

安装

1 make install CONFIG_PREFIX=/work/nfs_root/fs_mini_mdev_new

从交叉工具链安装glibc

1 cd /work/nfs_root/fs_mini_mdev_new
2 mkdir lib usr/lib
3 cp /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib/*so* lib/ -d
4 cp /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/usr/lib/*so* usr/lib/ -d

创建etc目录

1 mkdir etc

在etc目录下创建inittab文件

1 vim inittab    //内容从2-6行
2 # /etc/inittab
3 ::sysinit:/etc/init.d/rcS
4 console::askfirst:-/bin/sh
5 ::ctrlaltdel:/sbin/reboot
6 ::shutdown:/bin/umount -a -r

在etc目录下穿件init.d/rcS文件

 1 mkdir init.d
 2 vim init.d/rcS
chmod a+x init.d/rcS //设置为可执行文件
3 rcS文件内容如下: 4 #!/bin/sh 5 6 mount -a 7 mkdir /dev/pts 8 mount -t devpts devpts /dev/pts 9 echo /sbin/mdev > /proc/sys/kernel/hotplug 10 mdev -s

在etc目录下创建fstab文件:内容如下:

1 # device     mount-point    type   options        dump  fsck order
2 proc           /proc        proc   defaults        0     0
3 tmpfs          /tmp         tmpfs  defaults        0     0
4 sysfs          /sys         sysfs  defaults        0     0
5 tmpfs          /dev         tmpfs  defaults        0     0

创建dev目录:

1 mkdir dev
2 sudo mknod console c 5 1
3 sudo mknod null c 1 3

创建其他目录

1 mkdir proc tmp mnt sys root

编译制作jffs2映像文件的工具

1 tar -xvf mtd-utils-05.07.23.tar.bz2
2 cd mtd-utils-05.07.23/util
3 make 
4 sudo make install
//根据需要有可能要先编译zlib库

将fs_mini_mdev_new目录制作成文件系统镜像命令:

1 mkfs.jffs2 -n -s 2048 -e 128KiB -d fs_mini_mdev_new -o fs_mini_mdev_new.jffs2

在uboot中将文件系统和内核烧录进系统命令

1 nfs 30000000 192.168.1.100:/work/nfs_root/fs_mini_mdev_new.jffs2
2 nand erase.part rootfs
3 nand write.jffs2 30000000 260000 $filesize    
4 set bootargs console=ttySAC0,115200 root=/dev/mtdblock3 rootfstype=jffs2
5 //烧录内核
6 nfs 32000000 192.168.1.100:/work/nfs_root/uImage
7 bootm 32000000

启动系统,却报了如下的错误,这是由于我们的交叉工具链接在编译的时候是使用eabi接口的,内核也要使用这种接口才行

进入内核make menuconfig,加上EABI接口

重新烧录系统,成功进入系统没有问题。

 

 五、移植yaffs文件系统

下载yaffs文件系统的链接:https://yaffs.net/get-yaffs

或者使用git下载命令:

1 git clone git://www.aleph1.co.uk/yaffs2

将yaffs文件系统源码安装到内核:

./patch-ker.sh c m /home/zhuang/project/3-jz2440/systems/linux-3.4.2 

make menuconfig选中yaffs文件系统

 编译却报了如下的错误:

 查看内核struct mtd_info的定义,并没有sync成员而是_sync,把报错的地方都加上_即可。 

将文件系统制作成yaffs2文件系统镜像

1 mkyaffs2image fs_mini_mdev_new fs_mini_mdev_new.yaffs2

烧录启动:

1 nfs 30000000 192.168.1.100:/work/nfs_root/fs_mini_mdev_new.yaffs2
2 nand erase.part rootfs
3 nand write.yaffs 30000000 260000 $filesize

 

 

kernel2.6.32.2 mini2440 led 驱动 之 中断(去抖动)处理

kernel2.6.32.2 mini2440 led 驱动 之 中断(去抖动)处理

#include linux/module.h #include linux/init.h #include linux/mm.h #include linux/slab.h #include linux/types.h #include linux/semaphore.h #include linux/cdev.h #include linux/fs.h #include linux/semaphore.h #include linux/irq.h #include asm

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include
#include
#include

#define LED_MAJOR 250
#define LED_NAME "leds"

#define KEY_COUNT 4
#define KEY_DELAY HZ/50

DECLARE_WAIT_QUEUE_HEAD(r_wait);  //初始化等待队列
//wait_queue_head_t r_wait;
//init_waitqueue_head(&r_wait);
static struct timer_list key_timers[KEY_COUNT];  //定义定时器
static volatile bool idetify;

static void buttons_timer(unsigned long);

static unsigned long led_table [] = {
    S3C2410_GPB(5),
    S3C2410_GPB(6),
    S3C2410_GPB(7),
    S3C2410_GPB(8),
};//位地址

static unsigned int led_cfg_table [] = {
    S3C2410_GPIO_OUTPUT,
    S3C2410_GPIO_OUTPUT,
    S3C2410_GPIO_OUTPUT,
    S3C2410_GPIO_OUTPUT,
};//控制led为输入

struct button_irq_key{
    int irq;
    int pin;
    unsigned long flags;
    char *name;
};

static struct button_irq_key button_irqs[] = {
    {IRQ_EINT8, S3C2410_GPG(0), IRQF_TRIGGER_FALLING, "key1"},
    {IRQ_EINT11, S3C2410_GPG(3), IRQF_TRIGGER_FALLING, "key2"},
    {IRQ_EINT13, S3C2410_GPG(5), IRQF_TRIGGER_FALLING, "key3"},
    {IRQ_EINT14, S3C2410_GPG(6), IRQF_TRIGGER_FALLING, "key4"},
};

static volatile int press_cnt[] = {0, 0, 0, 0};
static volatile int pressed = 0;


struct leds_cdev
{
    struct cdev cdev;
    struct semaphore sem;
};

int leds_major = LED_MAJOR;
struct leds_cdev dev;

static irqreturn_t buttons_interrupt(int irq, void *dev_id)//按键中断处理函数
{
//    volatile int *press_count = (volatile int*)dev_id;
    
    //disable_irq(irq);
    int key = 0;
    
    switch(irq){
    case 52:
    key = 0;
    break;
    case 55:
    key = 1;
    break;
    case 57:
    key = 2;
    break;
    case 58:
    key = 3;
    break;
    default:break;

    }
    
    key_timers[key].data = key;
    mod_timer(&key_timers[key], jiffies + KEY_DELAY);
    printk(KERN_EMERG "irq success");
    return IRQ_HANDLED;
}


static void buttons_timer(unsigned long arg)//定时器中断处理函数,去抖实现
{
    int key = arg;
    int condition;
    int nf;
    condition = s3c2410_gpio_getpin(button_irqs[key].pin);
    if(!condition)
    {
    while(!s3c2410_gpio_getpin(button_irqs[key].pin));

    key_timers[key].expires = jiffies + KEY_DELAY;
    add_timer(&key_timers[key]);
    idetify = 1;    
    
    printk(KERN_EMERG "lower is appear");
    }else{
    
    if(!idetify)
    return;

    press_cnt[key] += 1;
    nf = press_cnt[key]%2;
    s3c2410_gpio_setpin(led_table[key], nf);

    pressed = 1;
    wake_up_interruptible(&r_wait);
    idetify = 0;
    
    printk(KERN_EMERG "up is appear%d", nf);
//    enable_irq(button_irqs[key].irq);
    }

//    printk(KERN_EMERG "success of interruptible");
}

static int led_buttons_open(struct inode *inode, struct file *filp)
{
    int i;
    int err;
    for(i = 0; i     err = request_irq(button_irqs[i].irq, buttons_interrupt,
                button_irqs[i].flags, button_irqs[i].name, (void*)&press_cnt[i]); //请求中断
    if(err)
    break;
/*    init_timer(&key_timers[i]);
    key_timers[i].data = i;
    key_timers[i].function = buttons_timer;
*/
    setup_timer(&key_timers[i], buttons_timer, i);初始化定时器
    }
    //key_timers[0].expires = jiffies + 3 * HZ;
    //add_timer(&key_timers[0]);
    if(err){
    i--;
    for(; i >= 0; i--)
    free_irq(button_irqs[i].irq, (void *)press_cnt[i]);
    return -EBUSY;
    }
    
    return 0;
}

static ssize_t led_buttons_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
{
    unsigned long err;
    wait_event_interruptible(r_wait, pressed);
    
    pressed = 0;
    
    err = copy_to_user(buf, (const void *)press_cnt, sizeof(press_cnt));
    memset((void *)press_cnt, 0, sizeof(press_cnt));
    return err ? -EFAULT : 0 ;
}

static int led_buttons_close(struct inode *inode, struct file *filp)
{
    int i;
    for(i = 0; i     free_irq(button_irqs[i].irq, (void*)&press_cnt[i]);//释放中断号
    del_timer(&key_timers[i]);
    }
    return 0;
}

static int leds_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
    switch(cmd)
    {
        case 0:
        if(arg > 4){
            return -EINVAL;
            }
        down(&dev.sem);
        s3c2410_gpio_setpin(led_table[arg], 0);
        up(&dev.sem);
        return 0;
        case 1:
        if(arg > 4){
        return -EINVAL;
        }
        down(&dev.sem);
        s3c2410_gpio_setpin(led_table[arg], 1);
        up(&dev.sem);
        return 0;
        default:
            return -EINVAL;
    }
}

struct file_operations cdev_fops = {
    .owner = THIS_MODULE,
    .ioctl = leds_ioctl,
    .open = led_buttons_open,
    .read = led_buttons_read,
    .release = led_buttons_close,
};

static void leds_setup(struct leds_cdev *dev, int index)
{
    int err, devno = MKDEV(leds_major, index);
    cdev_init(&dev->cdev, &cdev_fops);
    dev->cdev.owner = THIS_MODULE;
    dev->cdev.ops = &cdev_fops;
    err = cdev_add(&dev->cdev, devno, 1);
    if(err)
        printk(KERN_NOTICE "Error %d adding LED %d\n", err, index);
}

static int __init dev_init(void)       //module初始化与注册

{

    int result;
    int i;
//    struct class *my_class;
    dev_t devno = MKDEV(leds_major, 0);
    for(i = 0; i     s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
    s3c2410_gpio_setpin(led_table[i], 0);
    }
    
    if(leds_major)
    result = register_chrdev_region(devno, 1, "leds");
    else{
    result = alloc_chrdev_region(&devno, 0, 1, "leds");
    leds_major = devno;
    }
    
    if(result     return result;
    leds_setup(&dev, 0);
    sema_init(&dev.sem, 1);
/*    my_class = class_create(THIS_MODULE, LED_NAME);
    if(IS_ERR(my_class))
    {
    printk("Err:failed in creating class.\n");
    return -1;
    }
    device_create(my_class, NULL, MKDEV(leds_major, 0), LED_NAME, 0);
*/
    return 0;

}

void __exit dev_exit(void)
{
    cdev_del(&dev.cdev);
    unregister_chrdev_region(MKDEV(leds_major, 0), 1);
}

module_init(dev_init);
module_exit(dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("emperor");

MODULE_DESCRIPTION("LED MODULE");

自行分析

亲测通过

如果BUG,望大伙提出来,互相改善学习一下。

关于linux kernel mini2440 start.S head-common.S 部分注释linux no rule to make target needed by的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于arm Linux 系统启动之 ----start_kernel 函数、How to configure the Linux kernel/Loadable module support、jz2440-linux3.4.2-kernel移植【学习笔记】【原创】、kernel2.6.32.2 mini2440 led 驱动 之 中断(去抖动)处理等相关知识的信息别忘了在本站进行查找喔。

本文标签: