一垄青竹映陋室,半枝桃花衬碧窗.

Thursday, April 26, 2007

关于__setup 在内核中的作用

比如在printk.c有这样一句
__setup("console=",console_setup);
还有,在main.c中
__setup("root=",root_dev_setup);

答案如下:

你的这个问题,我从google上查找到了一些资料,再结合内核源代码,就在这里把这个问题说的清楚一点.
首先,这里有一个简短的回答,
http://mail.nl.linux.org/kernelnewbies/2003-03/msg00043.html

从这上面的意思是这里会从main.c 中的checksetup函数中运行,这个函数是这样的

static int __init checksetup(char *line)
{
struct kernel_param *p;

p = &__setup_start;
do {
int n = strlen(p->str);
if (!strncmp(line,p->str,n)) {
if (p->setup_func(line+n))
return 1;
}
p++;
} while (p < &__setup_end);
return 0;
}



这里的意思是从__setup_start开始处到__setup_end处中查找一个数据结构,这个数据结构中有str与setup_func这两个数据成员变量.
只要与这里面的str与输入的参数字符串相匹配,就会调用个这个字符串后面所指的内容,
对于你这里所说的 __setup("console=",console_setup); 就是你在启动linux内核的时候如果有这么一个参数输入console=ttyS1,那内核就会
把默认的tty定位为ttyS1,这个在consol_setup函数的字符串处理中完成,因为它最后是确定prefered_console的参数.


那把这在这里实现这个的内容是这样的,

__setup() 是一个宏定义,在include/linux/init.h这个文件中.
struct kernel_param {
const char *str;
int (*setup_func)(char *);
};

extern struct kernel_param __setup_start, __setup_end;

#define __setup(str, fn) \
static char __setup_str_##fn[] __initdata = str; \
static struct kernel_param __setup_##fn __attribute__((unused)) __initsetup = { __setup_str_##fn, fn }

在这个情景中作了替换是这样的

static char __setup_str_console_setup[] = "console=";
static struct kernel_param __setup_console_setup = { __setup_str_console_setup, console_setup}



这样你还可能不是很清楚,那你就要参考arch/i386/vmlinuz.lds这个关于ld 链接器的脚本文件有这样的一段

__setup_start = .;
.setup.init : { *(.setup.init) }
__setup_end = .;


这里的意思就是__setup_start是一个节的开始,而__setup_end是一个节的结束,这个节的名称是.setup,init,
这个你可以用readelf -a这个来看一下你的vmlinux-2.4.20-8(后面的数字与你的内核版本有关)这个文件,
可以看到有一个叫.setup.init的节,__setup_start就是指这个节的开始,那这个节中有什么内容呢,其实就是一个
数据结构,一个就是str,一个就是setup_func,与我前面的说法相一致,那具体是什么呢,就是一个在.init.data节中存储的
字符串-----__initdata是一个宏,就是(__attribute__ ((__section__ (".data.init")))), 所以你可以.data.init在vmlinux-2.4.20-8中的
在文件中的偏移量与加载的的虚拟地址偏移量相减就可以得到,
举个例子,所有的这些都是用readelf 与od 命令得到的
我现在用的内核版本,它的.setup.init的节在0x26dd60的文件偏移处.
[10] .data.init PROGBITS c0368040 268040 005d18 00 WA 0 0 32
[11] .setup.init PROGBITS c036dd60 26dd60 0001b0 00 WA 0 0 4

再查找console_setup在vmlinux-2.4.20-8所被映射为内存地址,
840: c0355d40 343 FUNC LOCAL DEFAULT 9 console_setup

这就可以知道了它所在的位置,就是0xc0355d40,这就是它的虚拟映射地址

再用下面一条命令
od --address-radix=x -t x4 vmlinux-2.4.20-8 |grep -A 20 26dd60 |head -20 | grep c0355d40
可以得到
26de40 c036943b c0355d10 c0369447 c0355d40

很明显,这个函数的处理字符串在内存中的地址是0xc0369447,与前面得到的.data.init节在内存映射中的位置
0xc0368040相减就是 0x1407,与.data.init在文件中的偏移量0x268040相加就得到0x269447
这样用
od --address-radix=x -a vmlinux-2.4.20-8 |grep -A 2 269440

就可以得到下面的内容,
269440 b l i n k = nul c o n s o l e = nul
269450 r e s e r v e = nul nul nul nul nul nul nul nul
269460 ` dc4 6 @ ` dc4 6 @ c p u f r e q =

"console="这个值果真就在这里.

(注:前面od 的选项 --address-radix= 表示的是显示文件偏移量的格式,默认下是o就是八进制, -t 表示显示文件二进制的形式
默认是o6 就是八进制的6位长,而-a表示显示的是字符串格式.)


这是一点感受,与大家分享,希望大家提出宝贵意见.

Saturday, April 21, 2007

一个关于__create_page_tables出问题的邮件

problems with __create_page_tables in arch/arm/kernel/head.S

Am Freitag, 7. Oktober 2005 21:23 schrieb Russell King - ARM Linux:
> On Fri, Oct 07, 2005 at 01:39:52PM +0200, Juergen Schindele wrote:
> > Hello guys,
> > I have a problem booting the kernel when
> > the part surounded with CONFIG_DEBUG_LL
> > in "__create_page_tables" is not enabled!
> > If this is not enabled the kernel is NOT BOOTING
> > (BDI shows
> > Current PC : 0xffff000c
> > Current CPSR : 0x800000d7 (Abort)
> > ) which seems to be an abort/fault vector.
> >
> > My problem is i dont know exactly what this code is doing
> > and i have a special memory mapping adviced from Nicolas Pitre
> > (see http://lists.arm.linux.org.uk/pipermail/
> > linux-arm-kernel/2005-July/030457.html)
> >
> > TEXTADDR is 0x80008000 !
> > and I use the BTUART (0x40200000)
> > as console on startup.
> > My kernel version is 2.6.12
> > with patches from Nicolas Pitre and Bill Gatliff
> >
> > What is the clean way to solve this problem ??
>
> No idea. There's no way to tell what's going on from your message.
> You say that there's an abort but you don't say which one. So,
> looking it up, it seems to be the prefetch abort.
>
> This is odd because anything mapped by the debug mappings should not
> be executed.
>
> > Is there an other problem which is not fixed ??
>
> No idea. Maybe the problem is related to your debugger. What happens
> if you don't use the debugger?

The helping hint came from Junjie Cai who told me that in "fixup" the memory
map was already used. I found in my machine_fixup a "pxa_gpio_mode()" which
is very bad here. I moved that code to machine_init and all is fine.
Thanks for your help.
--------------------------------------------------------------
Jürgen Schindele NENTEC Netzwerktechnologie GmbH
Entwicklung Greschbachstrasse 12
76229 Karlsruhe/Germany
eMail:schindele at nentec.de Phone: +49(0)721 94249-<51>
Web: www.nentec.de Fax: +49(0)721 94249-10
--------------------------------------------------------------
hi,
we had this similar problem before.
it was because that the mdesc->fixup was just
using the memory map created by DEBUG_LL .
and the peripheral meory map has not been created
before mdesc->map_io.

just for your information.
thanks.
On 10/7/05, Juergen Schindele <schindele at nentec.de> wrote:

entry-armv.S的分析

这里分析异常向量的处理:
在这个文件的最后一部分,是关于异常向量的处理代码,是很重要的。
基本的思路就是,对于有MMU的系统,异常向量的虚拟地址被映射到0xFFFF0000,
所以,真正的7个异常向量(__vectors_start~__vectors_end)是被拷贝到
这个0xFFFF0000开始的地方了。接着,异常处理代码块(__stubs_start~__stubs_end)
被拷贝到0xFFFF0200处。异常向量表的写法主要使用跳转指令B来进行,因为异常
向量表和异常处理代码块之间没有超过B指令要求的2^24=32MB,仅仅相差0x200。但是因为
vertor_swi不是在这个文件中定义的,所以,只能用LDR指令来进行处理了。实际上完成
上述拷贝动作是在start_kernel()[init/main.c]中调用trap_init()[arch/arm/kernel/traps.c]
来完成的。
void __init trap_init(void)
{
unsigned long vectors = CONFIG_VECTORS_BASE;
extern char __stubs_start[], __stubs_end[];
extern char __vectors_start[], __vectors_end[];
extern char __kuser_helper_start[], __kuser_helper_end[];
int kuser_sz = __kuser_helper_end - __kuser_helper_start;

/*
* Copy the vectors, stubs and kuser helpers (in entry-armv.S)
* into the vector page, mapped at 0xffff0000, and ensure these
* are visible to the instruction stream.
*/
memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);

/*
* Copy signal return handlers into the vector page, and
* set sigreturn to be a pointer to these.
*/
memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes,
sizeof(sigreturn_codes));

flush_icache_range(vectors, vectors + PAGE_SIZE);
modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
}
这里我们关注两个很有特色的的方:
其一,使用宏vector_stub来定义异常处理块,显得简洁明了。每种异常一进入,就需要根据
异常模式下的LR寄存器来获得发生异常时的PC,以及通过异常模式的SPSR来获得发生异常时的
CPSR;并且需要把这两个寄存器连同R0一道保存到异常模式下的SP所指向的堆栈区域中。保存
R0的原因就在于,R0会在后面保存发生异常的时的堆栈指针SP,并在进一步调用异常处理函数时被当
作参数。
其二,我们看到,只有irq,dabt,pabt,und等4个模式用了vector_stub,其他的还是按照自己的
写法进行。其中对于fiq,直接disable fiq并返回异常之前的模式地址;addrexcptn当死循环处理;
swi实际上是ARM Linux实现系统调用的;我们可以看到,异常向量表的reset异常就直接用了swi来进行
系统调用,并且调用号是SYS_ERROR0;这是对系统运行过程中发生reset异常时的处理方式,
和上电reset时bootloader进行的reset 异常处理是不一样的。

/*
* Vector stubs.
*
* This code is copied to 0xffff0200 so we can use branches in the
* vectors, rather than ldr's. Note that this code must not
* exceed 0x300 bytes.
*
* Common stub entry macro:
* Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
*
* SP points to a minimal amount of processor-private memory, the address
* of which is copied into r0 for the mode specific abort handler.
*/
.macro vector_stub, name, mode, correction=0
.align 5

vector_\name:
.if \correction
sub lr, lr, #\correction
.endif

@
@ Save r0, lr_ (parent PC) and spsr_
@ (parent CPSR)
@
stmia sp, {r0, lr} @ save r0, lr
mrs lr, spsr @LR实际上保存了发生异常时的CPSR,后面与0xF相与,实际上就是发生异常
@时的异常模式的编号,其值为0x0~0xF,但是只有0x0(user mode)和
@0x3(SVC mode)是可能的,其他的都是当作无效处理。
str lr, [sp, #8] @ save spsr

@
@ Prepare for SVC32 mode. IRQs remain disabled.
@
mrs r0, cpsr
eor r0, r0, #(\mode ^ SVC_MODE)@强制进入SVC模式
msr spsr_cxsf, r0

@
@ the branch table must immediately follow this code
@
and lr, lr, #0x0f
mov r0, sp
ldr lr, [pc, lr, lsl #2]@这里LR就是前面所说的发生异常时处理器模式的编号,
@以PC作为基地址,编号作为索引,将此后的内存区域(数组)
@中的相应异常的处理函数的地址加载到LR中,并通过下面的
@指令实现了跳转;所以,紧接vector_stub宏后面应该是一个
@处理函数的地址数组。
movs pc, lr @ branch to handler in SVC mode
.endm

.globl __stubs_start
__stubs_start:
/*
* Interrupt dispatcher
*/
vector_stub irq, IRQ_MODE, 4

.long __irq_usr @ 0 (USR_26 / USR_32)
.long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
.long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
.long __irq_svc @ 3 (SVC_26 / SVC_32)
.long __irq_invalid @ 4
.long __irq_invalid @ 5
.long __irq_invalid @ 6
.long __irq_invalid @ 7
.long __irq_invalid @ 8
.long __irq_invalid @ 9
.long __irq_invalid @ a
.long __irq_invalid @ b
.long __irq_invalid @ c
.long __irq_invalid @ d
.long __irq_invalid @ e
.long __irq_invalid @ f

/*
* Data abort dispatcher
* Enter in ABT mode, spsr = USR CPSR, lr = USR PC
*/
vector_stub dabt, ABT_MODE, 8

.long __dabt_usr @ 0 (USR_26 / USR_32)
.long __dabt_invalid @ 1 (FIQ_26 / FIQ_32)
.long __dabt_invalid @ 2 (IRQ_26 / IRQ_32)
.long __dabt_svc @ 3 (SVC_26 / SVC_32)
.long __dabt_invalid @ 4
.long __dabt_invalid @ 5
.long __dabt_invalid @ 6
.long __dabt_invalid @ 7
.long __dabt_invalid @ 8
.long __dabt_invalid @ 9
.long __dabt_invalid @ a
.long __dabt_invalid @ b
.long __dabt_invalid @ c
.long __dabt_invalid @ d
.long __dabt_invalid @ e
.long __dabt_invalid @ f

/*
* Prefetch abort dispatcher
* Enter in ABT mode, spsr = USR CPSR, lr = USR PC
*/
vector_stub pabt, ABT_MODE, 4

.long __pabt_usr @ 0 (USR_26 / USR_32)
.long __pabt_invalid @ 1 (FIQ_26 / FIQ_32)
.long __pabt_invalid @ 2 (IRQ_26 / IRQ_32)
.long __pabt_svc @ 3 (SVC_26 / SVC_32)
.long __pabt_invalid @ 4
.long __pabt_invalid @ 5
.long __pabt_invalid @ 6
.long __pabt_invalid @ 7
.long __pabt_invalid @ 8
.long __pabt_invalid @ 9
.long __pabt_invalid @ a
.long __pabt_invalid @ b
.long __pabt_invalid @ c
.long __pabt_invalid @ d
.long __pabt_invalid @ e
.long __pabt_invalid @ f

/*
* Undef instr entry dispatcher
* Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
*/
vector_stub und, UND_MODE

.long __und_usr @ 0 (USR_26 / USR_32)
.long __und_invalid @ 1 (FIQ_26 / FIQ_32)
.long __und_invalid @ 2 (IRQ_26 / IRQ_32)
.long __und_svc @ 3 (SVC_26 / SVC_32)
.long __und_invalid @ 4
.long __und_invalid @ 5
.long __und_invalid @ 6
.long __und_invalid @ 7
.long __und_invalid @ 8
.long __und_invalid @ 9
.long __und_invalid @ a
.long __und_invalid @ b
.long __und_invalid @ c
.long __und_invalid @ d
.long __und_invalid @ e
.long __und_invalid @ f

.align 5

/*=============================================================================
* Undefined FIQs
*-----------------------------------------------------------------------------
* Enter in FIQ mode, spsr = ANY CPSR, lr = ANY PC
* MUST PRESERVE SVC SPSR, but need to switch to SVC mode to show our msg.
* Basically to switch modes, we *HAVE* to clobber one register... brain
* damage alert! I don't think that we can execute any code in here in any
* other mode than FIQ... Ok you can switch to another mode, but you can't
* get out of that mode without clobbering one register.
*/
vector_fiq:
disable_fiq
subs pc, lr, #4

/*=============================================================================
* Address exception handler
*-----------------------------------------------------------------------------
* These aren't too critical.
* (they're not supposed to happen, and won't happen in 32-bit data mode).
*/

vector_addrexcptn:
b vector_addrexcptn

/*
* We group all the following data together to optimise
* for CPUs with separate I & D caches.
*/
.align 5

.LCvswi:
.word vector_swi

.globl __stubs_end
__stubs_end:

.equ stubs_offset, __vectors_start + 0x200 - __stubs_start

.globl __vectors_start
__vectors_start:
swi SYS_ERROR0
b vector_und + stubs_offset
ldr pc, .LCvswi + stubs_offset
b vector_pabt + stubs_offset
b vector_dabt + stubs_offset
b vector_addrexcptn + stubs_offset
b vector_irq + stubs_offset
b vector_fiq + stubs_offset

.globl __vectors_end
__vectors_end:

Friday, April 20, 2007

与2.6内核中platform结构相关

下面是一个人的原创文章:

================================

Author: taoyuetao
Email: tao_yuetao@yahoo.com.cn
Blog: http://www.eetop.cn/blog/?11145

2006-11-21

================================

从2.6版本开始引入了platform这个概念,在开发底层驱动程序时,首先要确认的就是设备的资源信息,例如设备的地址,
在2.6内核中将每个设备的资源用结构platform_device来描述,该结构体定义在kernel\include\linux\platform_device.h中,
struct platform_device {
const char * name;
u32 id;
struct device dev;
u32 num_resources;
struct resource * resource;
};
该结构一个重要的元素是resource,该元素存入了最为重要的设备资源信息,定义在kernel\include\linux\ioport.h中,
struct resource {
const char *name;
unsigned long start, end;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
下面举个例子来说明一下:
在kernel\arch\arm\mach-pxa\pxa27x.c定义了
tatic struct resource pxa27x_ohci_resources[] = {
[0] = {
.start = 0x4C000000,
.end = 0x4C00ff6f,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_USBH1,
.end = IRQ_USBH1,
.flags = IORESOURCE_IRQ,
},
};
这里定义了两组resource,它描述了一个usb host设备的资源,第1组描述了这个usb host设备所占用的
总线地址范围,IORESOURCE_MEM表示第1组描述的是内存类型的资源信息,第2组描述了这个usb host设备
的中断号,IORESOURCE_IRQ表示第2组描述的是中断资源信息。设备驱动会根据flags来获取相应的资源信息。
有了resource信息,就可以定义platform_device了:
static struct platform_device ohci_device = {
.name = "pxa27x-ohci",
.id = -1,
.dev = {
.dma_mask = &pxa27x_dmamask,
.coherent_dma_mask = 0xffffffff,
},
.num_resources = ARRAY_SIZE(pxa27x_ohci_resources),
.resource = pxa27x_ohci_resources,
};
有了platform_device就可以调用函数platform_add_devices向系统中添加该设备了,这里的实现是
static int __init pxa27x_init(void)
{
return platform_add_devices(devices, ARRAY_SIZE(devices));
}
这里的pxa27x_init必须在设备驱动加载之前被调用,可以把它放到
subsys_initcall(pxa27x_init);

驱动程序需要实现结构体struct platform_driver,参考kernel\driver\usb\host\ohci-pxa27.c,

static struct platform_driver ohci_hcd_pxa27x_driver = {
.probe = ohci_hcd_pxa27x_drv_probe,
.remove = ohci_hcd_pxa27x_drv_remove,
#ifdef CONFIG_PM
.suspend = ohci_hcd_pxa27x_drv_suspend,
.resume = ohci_hcd_pxa27x_drv_resume,
#endif
.driver = {
.name = "pxa27x-ohci",
},
};

在驱动初始化函数中调用函数platform_driver_register()注册platform_driver,需要注意的是
ohci_device结构中name元素和ohci_hcd_pxa27x_driver结构中driver.name必须是相同的,这样
在platform_driver_register()注册时会对所有已注册的所有platform_device中的name和当前注
册的platform_driver的driver.name进行比较,只有找到相同的名称的platfomr_device才能注册
成功,当注册成功时会调用platform_driver结构元素probe函数指针,这里就是ohci_hcd_pxa27x_drv_probe。

当进入probe函数后,需要获取设备的资源信息,获取资源的函数有:
struct resource * platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num);
根据参数type所指定类型,例如IORESOURCE_MEM,来获取指定的资源。
struct int platform_get_irq(struct platform_device *dev, unsigned int num);
获取资源中的中断号。
struct resource * platform_get_resource_byname(struct platform_device *dev, unsigned int type, char *name);
根据参数name所指定的名称,来获取指定的资源。
int platform_get_irq_byname(struct platform_device *dev, char *name);
根据参数name所指定的名称,来获取资源中的中断号。
-----------------------------------------
下面是一个人回答别人的问题时做的说明:
呵呵,这个问题是这样的

首 先你需要为SOC的各个功能部分定义他的一些资源.例如可用于访问的寄存器地址.中断号,DMA什么的。然后将这些资源(resource) 作为 platform 的dev .通过platform_add_devices函数将你定义的paltform_device变量注册到系统的dev里面.。或者你可以象我这样将你需 要的驱动添加:
static struct platform_device *smdk2410_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_bl,
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
&s3c_device_sdi,
&s3c_device_adc,
&s3c_device_nand,
&s3c_device_usbgadget,
&s3c_device_ts,
&s3c_device_buttons,
&s3c_device_rtc,
&s3c_device_spi0,
&s3c_device_timer1,//add by cefanty for battery charging
};
这样你的硬件的信息和资源就会注册到系统中.

说了半天,这回该说这有什么用了。
你编写的驱动或者移植别人的驱动,一般在驱动里有这样的代码,例如:
static struct platform_driver s3c2410sdi_driver =
{
.probe = s3c2410sdi_probe,
.remove = s3c2410sdi_remove,
.suspend= s3c2410mci_suspend,
.resume= s3c2410mci_resume,
.driver={
.name= "s3c2410-sdi",
.bus = &platform_bus_type,
.owner= THIS_MODULE,
},
};

看到 .name= "s3c2410-sdi",这条关键的语句没有??,它和我在上面注册的&s3c_device_sdi,里的device的名称是一致的.我这里展开我的s3c_device_sdi,的内容
:
/* SDI */

static struct resource s3c_sdi_resource[] = {
[0] = {
.start = S3C2410_PA_SDI,
.end = S3C2410_PA_SDI + S3C24XX_SZ_SDI - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_SDI,
.end = IRQ_SDI,
.flags = IORESOURCE_IRQ,
},
[2] = {
.start = 3,
.end = 3,
.flags = IORESOURCE_DMA,
}
};

struct platform_device s3c_device_sdi = {
.name = "s3c2410-sdi",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_sdi_resource),
.resource = s3c_sdi_resource,
};

在驱动程序里的init代码大致如下:
static int __init s3c2410sdi_init(void)
{
return platform_driver_register(&s3c2410sdi_driver);
}
用platform_driver_register 向系统注册这个驱动程序.而这个函数会在s3c2410sdi_driver的信息里提取name为搜索内容,搜索系统注册的device中有没有这个 platform_device。 如果有注册,那么接着会执行platform_driver 里probe函数.在这里显然是s3c2410sdi_probe函数 在probe函数里,用的最多和刚才platform_device有关的语句是platform_get_resource,这条语句用于获取 platform_device里的resource资料.例如映射的IO地址,中断等.剩下等得就是ioremap,和 request_irq等的事情了

::))))


Sunday, April 15, 2007

一个关于booting sequence documentation的邮件

> due to compiling problem, i try to understand the boot sequence. I know
> the main points for i386 architecture.
>
> i assume that the entry point of Linux is the
> ~/arch/armnommu/boot/compressed/head.S file written by Russel King.
> This function call (after uncompressing the kernel) the "start_kernel"
> function.
>
> i wish to know:
>
> * what is the real boot sequence? (if i am wrong)
> * what exactly do the head.S file? (or where is documentation about it?)

I've been collecting info with intent to write this up properly. I haven't
done it yet, but I might soon.

Here is the collected info, totally unedited, and largely from posts to this
list. It should help you get the general idea:

Startup
-------

bootloader:
Linux needs almost nothing. Basically Linux just needs a boot loader to
get the kernel from some storage medium into RAM by some method, set up
a couple of registers and call it at its entry point. No MMU setup is
required.

After the bootloader finishes, it calls bootLinux(), which jumps to
the kernel. However, since the kernel is compressed, the first
entry point is in arch/arm/boot/compressed/head.s. This calls
decompress_kernel(), which is located in /arch/arm/boot/compressed/misc.c.
This calls setup_sa1100(), which initializes the UART and GPIO, and then
calls gunzip() after printing "Uncompressing Linux".


Boot code:
> ./boot/compressed/head.S
> ./boot/compressed/head-sa1100.S
> ./boot/compressed/setup-sa1100.S

This is startup code for decompressing and locating the kernel image. The
kernel is stored in compressed form to save space and make bootstrapping
simpler and easier. This code is discarded once the kernel begins
executing. This code is called by the bootloader program or whatever is
used to initialize the machine following a hard reset, after the bootloader
has loaded the compressed kernel image to a specified location in system
memory.

> ./kernel/head-armo.S
> ./kernel/head-armv.S

This is the entry point for the kernel itself, which is entered following
the decompression and relocation of the kernel image to its final
destination area in system memory.It uses the machine number in R1 to find a
table built by the linker containing vital info like where the RAM is, how
much of it there is etc.

----from RMK:--
On Tue, Jul 17, 2001 at 08:45:15PM -0700, Yang, Neil L wrote:
> I'm interested in finding out more about the arm boot sequence. One of the
> questions I had was where decompress_kernel() is called from.

arch/arm/boot/compressed/head.S

> Also, when is the MACHINE_START macro called?

Not every macro is a piece of executable code. This particular one builds
a data structure.


On Wed, Jul 18, 2001 at 09:49:57AM -0700, Chivukula, Sandeep wrote:
> >> Also, when is the MACHINE_START macro called?
> >Not every macro is a piece of executable code. This particular one builds
> >a data structure.
>
> So when in the boot process does this data structure get created and by
> whom ? i.e. which function makes it initially

No function makes it. I'll explain more clearly.

The MACHINE_START macro expands to a statically built data structure, ie one
that is there when the compiler builds the file. It expands to:

const struct machine_desc __mach_desc_(type)
__attribute__((__section__(".arch.info"))) = {
nr: MACH_TYPE_(type),
name: (name)

The various other definitions set various machine_desc structure members
using named initialisers, and finally MACHINE_END provides the closing
brace for the structure initialiser. (see include/asm-arm/mach/arch.h)

Note that above, (type) is the first argument passed to the MACHINE_START
macro, and (name) is the second.

All of these machine_desc structures are collected up by the linker into
the .arch.info ELF section on final link, which is bounded by two symbols,
__arch_info_begin, __arch_info_end. These symbols do not contain pointers
into the .arch.info section, but their address are within that section.

I hope this is enough information.

-----------

This code calls start_kernel

-------From: Ray L <rayl@otii.com>-----on linux-arm-kernel list
> Is there a way
> to figure out where everything is placed in the kernel zImage, by looking
> at the setup of the makefiles and ld?

yes, look at the ldscripts (*.lds.* under linux/arch/arm/)


> Where are the variables _text,
> _etext, _edata, and _end defined.

in one of the ldscript files, eg: linux/arch/arm/vmlinux.lds

this script also defines a few tables, including:

proc.info is an array of structs collected from places like the end of
linux/arch/arm/mm/proc-arm720.S. these structs hold processor-specific info.

arch.info is an array of 'struct machine_desc' collected from the
MACHINE_START() macros, in places like linux/arch/arm/mach-clps711x/p720t.c.
these structs hold machine-specific info.



> Specifically, I would like to know the actual trace of the boot
> sequence of the kernel on an arm processor, not an x86.

ok, assume you're on a P720T.

- assume the uncompressor has done its thing, and we have, in RAM, a vmlinux
image ready to go

- according to linux/arch/arm/vmlinux.lds, the ENTRY() is at 'stext', so go
to linux/arch/arm/head-armv.S and find the stext symbol

- you're not on a netwinder or L7200 so skip down to __entry

- disable interrupts and make sure we're in supervisor mode

- call lookup_processor_type, which will query the cpu for it's id, then
lookup that id in .proc.info table. if it finds the struct, r10 will point
to it. this struct comes from the end of linux/arch/arm/mm/proc-arm720.S.

- if we didn;t find the struct, print out 'Error: p' on uart2 and die.

- the bootloader has arranged for r1 to hold a special integer, selected from
the list in linux/arch/arm/tools/mach-types. this will be 24 for the P720T

- call lookup_architecture_type, which will lookup r1 in the arch.info
table. this is a 'struct machine_desc' created in MACHINE_START() in
linux/arch/arm/mach-clps711x/p720t.c.

- if we didn;t find the struct, print out 'Error: a' on uart2 and die.

- call __create_page_tables to map in ram, uarts, etc

- set up lr with __ret. next time we return from a subroutine, we'll return
to __ret, which is a few lines down.

- set pc = [r10 + 12]. in other words, jump to the address in the fourth
slot of the P720T proc.info struct, which is __arm720_setup in
linux/arch/arm/mm/proc-arm720.S.

- __arm720_setup sets up MMU and returns to __ret in head-armv.S

- at __ret, set up lr with __mmap_switched (via __switch_data)

- turn on the MMU, wait for the pipeline to clear, and return to
__mmap_switched a few lines down

- __mmap_switched loads up the info from __switch_data. this, among other
things, set up stack pointer to inittask + 8192.

- clear out BSS

- jump to start_kernel




hopefully i got all that correct :-) it's not the easiest thing to follow,
but pretty straightforward once you know where the tricky jumps and linker
tables are.

--------endsnip

Wookey
--
Aleph One Ltd, Bottisham, CAMBRIDGE, CB5 9BA, UK Tel +44 (0) 1223 811679
work: http://www.aleph1.co.uk/ play: http://www.chaos.org.uk/~wookey/

Saturday, April 14, 2007

关于ARM Linux的meminfo的设定

这来自于在
http://www.linuxforum.net/forum/showflat.php?Cat=&Board=embedded&Number=638456&page=0&view=collapsed&sb=5&o=all&fpart=all
上的帖子,我也顺着看了一下。
问题:
经过定义之后
static struct meminfo meminfo __initdata = { 0, };
我们可以看到初始化为0,然后没进行什么赋值操作,到调用
paging_init(&meminfo, mdesc);
的时候,meminfo里面已经含有内存信息,如何赋值的?
一种意见是:
static struct meminfo meminfo __initdata = { 0, };
2.4 & 2.6 这个定义都一样进行初始化,我认为关键是 __initdata 的用法了

static struct init_tags {
struct tag_header hdr1;
struct tag_core core;
struct tag_header hdr2;
struct tag_mem32 mem;
struct tag_header hdr3;
} init_tags __initdata = {
{ tag_size(tag_core), ATAG_CORE },
{ 1, PAGE_SIZE, 0xff },
{ tag_size(tag_mem32), ATAG_MEM },
{ MEM_SIZE, PHYS_OFFSET },
{ 0, ATAG_NONE }
};

所以这儿已经指定了缺省的变量值了,这里面就有MEM_SIZE=16M
只是这个变量同meminfo变量是怎么关联起来的,这种用法还比较少见
望哪位高兄来高见一翻!
这里仍然没有根本解决,但是点到了默认初始化的地方。
另外一种说法比较详细:
三种方式:
1.使用内核源代码中的定义
通常ARM嵌入式开发板的系统物理内存仅有一块,系统物理内存的指定通常通过三个宏:
PHYS_OFFSET:系统内存的物理起始地址,板级相关,在include/asm-arm/arch-xxx/memory.h中
PAGE_OFFSET:系统内存的虚拟起始地址,体系结构相关,为0xC0000000,在include/asm-arm/memory.h中
MEM_SIZE:系统内存大小,如果板级(include/asm-arm/arch-xxx/xxx.h)没有指定,则使用体系结构的缺省值16M(arch/arm/kernel/setup.c)
在没有内存命令从内核命令行(cmdline)传入的情况下 ,setup_arch()函数 (arch/arm/kernel/setup.c文件中)会使用PHYS_OFFSET和MEM_SIZE指定为系统内存并且映射到PAGE_OFFSET。

2.通过fixup函数设置
也可通过fixup_xxx()函数(arch/arm/mach-xxx/arch.c)设置内存的起始地址和大小,映射到PAGE_OFFSET,此项设置会覆盖(1)的设置

3.通过内核命令行传入
如果内核命令行中有系统内存相关的命令(mem=XXXM[@YYY])则会覆盖掉(1),(2)中所介绍的映射而将XXX作为内存大小,YYY作为内存物理起始地址(如果有)映射到PAGE_OFFSET

以上是2.4内核,2.6内核基本差不多 。
并且指定了查看的路径:
大概如下:
setup_arch()->parse_tags()->parse_tag()->parse_tag_mem32()->arm_add_memory()
经过查找,的确如此。

Friday, April 13, 2007

关于FC5上tftp的设定

我遇到的问题是出现Permission denied 的问题,这个以前在用RH9时时没有遇到过的。
最后发现用
#chmod 777 /tftpboot
#chmod 777 file-to-be-read
#xinetd -c
之后可以解决。