本文的目的是介绍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)
- 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)
内核版本: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 函数
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
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
作者:庄泽彬(欢迎转载,请注明作者)
说明:韦东山二期视频学习笔记
交叉编译工具: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 驱动 之 中断(去抖动)处理
#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 驱动 之 中断(去抖动)处理等相关知识的信息别忘了在本站进行查找喔。
本文标签: