嵌入式内容(转)
时间:2006-07-07 来源:wwling2001
uClinux下移植Ne2000兼容的网卡驱动程序
还是2.5年前写的老文章,拿了过来凑数了。这两天在写一个书稿(关于嵌入式Linux驱动程序和相关硬件的),每天敲键盘,敲得手疼,这里的更新就先吃老本了... uClinux下移植Ne2000兼容的网卡驱动程序(1)
我是linux的新手,可以说从来没有在linux下写过程序,对于linux内核也是相当陌生,前一段时间,拿着tpu一个移植好了的uClinux在S3C44B0(ARM7TDMI核的嵌入式处理器)上的版本,把它成功的跑在了我自己的S3C44B0的板子上,这也就算是平生在uClinux下作的第一个工作吧。接下来就是添加网卡驱动,我用的是RTL8019AS——比较常用的ISA接口的以太网芯片。下面就从一个新手的角度来说说我的移植过程吧,其实很简单,我的整个摸索+移植的过程也就花了2天的时间,我尽量写的详细(罗嗦?)一点,希望对像我这样的新手入门有所帮助,错误之处在所难免,欢迎指正。 开始的时候,我也是摸不着头脑,不知道该从什么地方入手。用SoureInsight把整个uClinux内核的源码都添加进来,熟悉一下linux的内核(其实就是在里面瞎撞,也不怎么能看懂)。按照linux内核目录的分类,很自然的就找到Ne2000网卡的驱动就是./drivers/net/ne.c,和它相关的还有8390.h和8390.c。看看代码,逐渐的就明白了: 首先,在Ne.c中函数ne_probe就是网卡的检测函数,如果检测到Ne2000兼容的网卡就return 0。那个函数没有什么具体的工作,就是搭了一个架子。看的有前人在这个函数开始写到:
#if defined (CONFIG_NETtel) && defined (CONFIG_M5307)
…………
#elif defined(CONFIG_COLDFIRE)
static int once = 0;
if (once)
return -ENXIO;
if (base_addr == 0) {
dev->base_addr = base_addr = NE2000_ADDR;
dev->irq = NE2000_IRQ_VECTOR;
once++;
}
#endif
就明白了,可以把网卡的基地址、中断号都放到这里面定义。我也跟着照葫芦画瓢,添加了一个:
#elif defined(CONFIG_ARCH_S3C44B0) //--by threewater
static int once = 0;
if (once)
return -ENXIO;
if (base_addr == 0) {
dev->base_addr = base_addr = ARM_NE2000_BASE;
dev->irq = ARM_NE2000_IRQ;
once++;
}
其中:ARM_NE2000_BASE和ARM_NE2000_IRQ是在配置内核的时候定义的,这个以后再说。 接下来,具体的工作就转移到了ne_probe1函数里面做。用SourceInight跟进来看(这个软件太好用了,忍不住在这里再坐一会广告)。Ne_probe1中,一开始就是
reg0 = inb_p(ioaddr);
if (reg0 == 0xFF) {
ret = -ENODEV;
goto err_out;
}
很容易理解,就是读一下网卡的基地址,对我来说也就是RTL8019的REG0,如果是0xff,说明没有检测到网卡,返回错误。好了,在下面添加一行
printk("begin find Ne2000 Net Card...\tbase address=0x%X\n",ioaddr);
//--add by threewater
来证明我们的想法是正确的,程序如果能读取8019的REG0,就应该显示出这一行。可是,那个ne_probe是谁调用的呢?还是用SourceInsight去找,用jamp to caller,哈哈,太容易了,立刻就看到了,网卡的检测是从./drivers/net/Space.c的ethif_probe函数中实现的,关键代码:
if (probe_list(dev, eisa_probes) == 0)
return 0;
eisa_probes在前面定义成全局:
static struct devprobe eisa_probes[] __initdata = {
#ifdef CONFIG_DE4X5 /* DEC DE425, DE434, DE435 adapters */
{de4x5_probe, 0},
#endif
…………
{NULL, 0},
};
我也照着添加了:
if (probe_list(dev, arm_probes) == 0)
return 0;
并定义:
static struct devprobe arm_probes[] __initdata = {
#ifdef CONFIG_ARM
{ne_probe, 0},
#endif
{NULL, 0},
};
这样,编译内核启动,果然,显示出了输出结果。
(待续) uClinux下移植Ne2000兼容的网卡驱动程序(2)
继续分析修改ne.c中ne_probe1的代码(关键的东东全在这里面呢)。接下来就是
outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
regd = inb_p(ioaddr + 0x0d);
outb_p(0xff, ioaddr + 0x0d);
读取REGD中的数据,这里,再仔细跟踪一下outb_p这个函数,在x86中,这个就是一个IO口的输出函数,在S3C44B0是存储器和IO统一编址的(或者说不分存储器还是IO),经过了几次宏定义以后,很快找到如下宏代码:
(*(volatile unsigned char *)(a))
和我想的一样,就是靠这个访问外部总线的。我的8019在S3C44B0的Bank 5上,工作在跳线模式,算了一下,起始基地址就是0x0a000600。 这里,需要说明一下我的硬件配置和连接,8019工作在16位模式下,S3C44B0的Bank5配置成16位模式,数据线一对一的连接,地址线错开一位——8019的A0连接S3C44B0的A1……这样,8019的基地址(Reg0的地址)是0x0a000600,Reg1的地址就是0x0a000602……地址不是连续增加的,所以,对应的驱动程序要做相应的修改。查找E8390_CMD的定义,发现,在8390.h中有: #define E8390_CMD EI_SHIFT(0x00) /* The command register (for all pages) */
/* Page 0 register offsets. */
#define EN0_CLDALO EI_SHIFT(0x01) /* Low byte of current local dma addr RD */
#define EN0_STARTPG EI_SHIFT(0x01) ……
而EI_SHIFT根据不同的配置有两种定义,如下: #if defined(CONFIG_MAC) || defined(CONFIG_AMIGA_PCMCIA) || \
defined(CONFIG_ARIADNE2) || defined(CONFIG_ARIADNE2_MODULE) || \
defined(CONFIG_HYDRA) || defined(CONFIG_HYDRA_MODULE) || \
defined(CONFIG_ARM_ETHERH) || defined(CONFIG_ARM_ETHERH_MODULE)
#define EI_SHIFT(x) (ei_local->reg_offset[x])
#else
#define EI_SHIFT(x) (x)
#endif 看来,在8390的驱动中已经考虑到了不连续增长的地址的问题了,继续跟踪查看ei_local->regoffset[x]的定义就比较麻烦了。干脆,我用一个笨方法,直接添加: #elif defined(CONFIG_ARM) || defined(CONFIG_ARM_MODULE) //--by threewater
#define EI_SHIFT(x) ((x)*2) 对应的,在ne.c也有类似的定义问题:
#define NE_CMD 0x00
#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
#define NE_IO_EXTENT 0x20
添加成:
#ifdef CONFIG_ARM //--by threewater
#define NE_CMD 0x00
#define NE_DATAPORT 0x20 /* NatSemi-defined port window offset. */
#define NE_RESET 0x3e /* Issue a read to reset, a write to clear. */
#define NE_IO_EXTENT 0x40
#else
……
这样,地址偏移的问题就基本解决了。当然,在Ne.c中,也有直接访问reg的代码,比如上面说的代码也相应的添加成:
#ifdef CONFIG_ARM //--add by threewater
regd = inb_p(ioaddr + 0x0d*2);
outb_p(0xff, ioaddr + 0x0d*2);
#else
regd = inb_p(ioaddr + 0x0d);
outb_p(0xff, ioaddr + 0x0d); 我没有看过linux编程的规范,也不知的修改内核有什么规矩,不过,我都是用预处理来添加我自己的代码,从来不直接在原有的代码上修改,我觉得这样更可以保证代码的完整性和可移植性,而且,还容易比较,容易找出问题(当然,如果#if嵌套多了,也很难看的:()。 接下来的初始化8019,就没有什么问题了,然后就是配置网卡的物理地址了。在我的系统上,没有使用8019的初始化配置芯片,物理地址需要在程序中直接写入(其实,就是使用配置芯片,也需要用程序读出再写入的),物理地址可以编译到代码里,也可以存储到flash的一个固定地址中。可以参考ne_probe1里面的例子,照着勒就可以了。剩下注册中断什么的,也就是算好了中断号,照着添加自己的代码。很容易的。 到这里,似乎就没有什么工作了。编译内核,启动,恩Ne2000兼容的网卡找到了,接下来就不正常了。系统报告,反复陷入那个网卡的中断……
(待续) uClinux下移植Ne2000兼容的网卡驱动程序(3)
反复陷入中断,很容易想到就是中断模式配置的问题,8019的中断是高电平有效,看看S3C44B0上的配置,果然不对。这个配置是在Bootloader中做好了的,改一下,就好了。我把他改成了上升沿触发。 另外,因为S3C44B0是IO空间和存储器统一编址。这就容易忽视一个问题,就是缓冲。对于存储器,加上片内的缓冲可以提高效率,不过对于外部设备比如这个8019,就不能使用缓冲。记住,缓冲的范围一定要配置正确,我开始就弄错了,产生了一些莫名其妙的问题,耽误了不少时间。 上述问题都解决了,启动的时候可以找到网卡,可以配置好物理地址,启动以后ifconfig eth0也没有问题,这次应该没有问题了吧。可是,结果还是ping通。这次就比较麻烦了,没有内核耕种调试的手段,只能靠printk来输出?不知道应该从哪里入手了。不过还好,调试以太网,有Sniffer(一个功能强大的抓包软件,在局域网内的数据包都是抓到)。就靠它了,在我的PC上运行,抓包。在uClinux下ping主机的IP。果然能抓到数据包。分析抓取的数据包发现问题。 按理说,ping的时候,第一次不知道目的主机的Mac地址,所以,应该发送ARP广播,发送的数据大概的格式开头应该是FF FF FF FF FF FF AA BB CC DD EE FF…………(AA BB CC DD EE FF表示发送方的Mac地址),可是我抓到的数据包是FF FF FF FF FF FF AA AA BB BB CC CC DD DD EE EE FF FF…… 看明白了,这个问题应该是网卡发送的时候,向网卡写入数据连续写了两次。这个问题最容易让人想到是S3C44B0的挂8019的那个Bank的数据宽度配置错了。可是,我仔细的看了,不是这个问题。那就只有再仔细看看源码了。还是在drivers/net/ne.c里面,ne_block_output函数——这个就是8019发送时候调用的函数了,里面有代码:
if (ei_status.word16) {
outsw(NE_BASE + NE_DATAPORT, (void*)buf, count>>1);
} else {
outsb(NE_BASE + NE_DATAPORT, (void*)buf, count);
}
我跟踪了一下,ei_status.word16=1,这个没有问题。那么,问题就出在outsw函数上了。用SourceInsight一层层的跟踪(做一个函数右一个宏的,定义的可真多,好多不同模式或者处理器下的相同定义,要看清楚自己的),最后,终于把目标锁定在了arch/armnommu/lib/ io-writesw-armv3.S和io-writesw-armv4.S两个汇编文件。到底是哪个呢? 熟悉ARM家族的人应该知道ARMv3和ARMv4的一些区别,看看这两汇编,就可以开出来他们对16位数读写操作的不同,按照道理S3C44B0应该是ARMv4(我记得应该是,不到出处了,至少看了那个两汇编文件,我认定应该用ARMv4那个),可是,看了一下便一输出的.o文件,是io-writesw-armv3.o,显然弄错了,这里就是问题了。那么为什么要编译ARMv3而不是ARMv4这个文件呢?在Makefile和Config.in中经过一番寻找,终于找,原来在定义arch/armnommu/config.in中,定义CONFIG_ARCH_S3C44B0的时候,没有定义 CONFIG_CPU_32v4 那么,默认情况下,就定义CONFIG_CPU_32v3,用它来编译。好了。把ARMv4的定义添上。顺便把前面说的ARM_NE2000_BASE和ARM_NE2000_IRQ的定义以添加到这里,让用户可以自己定义裁剪。 hex 'Base Address for NE2000 ethernet' ARM_NE2000_BASE youraddr
hex 'IRQ for NE2000 ethernet' ARM_NE2000_IRQ yourinterrupt 好了,编译通过。运行,果然没有问题了。Ping可以,telnet可以,在内核中把NFS打开,mount –t nfs ……也好用。哈哈。太好了。至此,8019在S344B0组成的uclinux平台上的驱动,移植成功。相信其他的网卡芯片一直驱动程序也基本是这个思路。现在写出来与大家共享。 现总结一下,移植过程中需要注意的几个问题: 1、确定网卡的基地址、中断无误
2、注意网卡的数据总线宽度,地址是否连续,如果不连续,如何映射
3、注意网卡的中断的模式和处理对应的外部中断是不是一致
4、对于IO和RAM统一编址的处理器,注意缓冲区范围的设置
5、注意ARMv3和ARMv4等一些和处理器结构相关的底层函数库带来的问题
6、用抓包软件可以帮助分析定位问题所在 Btw,我的PC平台是在WindowsXP+Virtual PC下安装的Red Hat linux 8.0,我觉得这样调试起来比较方便,可以用SoureInsigh来阅读,编写代码,可以在Linux编译。充分发挥两个操作系统的优势。很适合于像我这样的,不熟悉Linux人开发。
11月5日
image.ram、image.rom、romfs.img(2)
-- 作者:lamond
-- 发布时间:2004-11-10 16:12:00 -- 问一个image.rom 的问题 环境:S3C4510 + uClinux-dist-20030522 我在 uClinux/images 目录下编译生成了3个文件:分别是: image.ram(2518K)、image.rom(1211K)、romfs.img(1377K)。 image.ram是ram版内核,image.rom是rom版经过压缩的内核, 请问image.ram和image.rom中是否都包含了romfs文件系统?可生成的romfs.img文件比 image.rom还大,难道说romfs.img也被压缩进image.rom了? --------------------------------------------------------------------------------
-- 作者:raodali
-- 发布时间:2004-11-10 20:39:00 -- 回复
不,image.ram/image.rom和romfs.img是互相独立的
--------------------------------------------------------------------------------
-- 作者:ryansheng
-- 发布时间:2004-11-10 21:01:00 --
可以包含也可以不包含,取决于链接方式如何配置。romfs.img被压缩进image.rom是完全可能的。
--------------------------------------------------------------------------------
-- 作者:lamond
-- 发布时间:2004-11-11 12:27:00 -- 谢谢。 我研究一下image.rom的链接方式。 作者:redsun
-- 发布时间:2003-12-31 22:11:35 -- 请问如何生成image.rom 和 image.ram
我去uclinux的网站下载了uClinux,可以生成 image.bin 和 romfs.img,但是要生成image.rom 和 image.ram还需要什么呢? --------------------------------------------------------------------------------
-- 作者:xcpzd
-- 发布时间:2004-1-2 9:25:31 --
我也是看别人的,我也有同样的问题,whaler是没有执行下面的第一步
1、进入linux-2.4.x/arch/armnommu/boot/compressed目录执行
ln -s vmlinux.lds.in linux.lds.in(链接一个编译bzImage需要的文件实际上我的rom版跑出来还有问题,但为了编译还是要这一步) 2、进入linux-2.4.x/arch/armnommu目录,修改config.in里面存储器参数,原为8M DRAM,改为16M。
修改vmlinux-armv.lds.in,在
*(.got) /* Global offset table */ 下加上
romfs_data = .;
romfs.o
romfs_data_end = .; 3、进入linux-2.4.x/drivers/block目录,修改blkmem.c,定义一个
#ifdef CONFIG_BOARD_SNDS100
extern char romfs_data[];
#endif
再在
struct arena_t {
.........
} arena[] = { 后加上 #ifdef CONFIG_BOARD_SNDS100
{0, romfs_data, -1},
#endif 4、修改uClinux-dist目录下(即总的编译目录)的Makefile,在
TFTPDIR = /tftpboot 下加一行
LINUXTARGET = bzImage 5、修改vendors/Samsung/4510B/Makefile,原来的
-IMAGE = $(IMAGEDIR)/image.bin
-ELFIMAGE = $(IMAGEDIR)/image.elf
两行改为
ROMIMAGE = $(IMAGEDIR)/image.rom
RAMIMAGE = $(IMAGEDIR)/image.ram
并取消其中最后连接的命令
$(CROSS_COMPILE)objcopy -O binary --remove-section=.romvec --remove-section=.text --remove-section=.ramvec --remove-section=.init --remove-section=.bss --remove-section=.eram $(ROOTDIR)/$(LINUXDIR)/linux $(IMAGEDIR)/linux.data
$(CROSS_COMPILE)objcopy -O binary --remove-section=.ramvec --remove-section=.bss --remove-section=.data --remove-section=.eram --set-section-flags=.romvec=CONTENTS,ALLOC,LOAD,READONLY,CODE $(ROOTDIR)/$(LINUXDIR)/linux $(IMAGEDIR)/linux.text
cat $(IMAGEDIR)/linux.text $(IMAGEDIR)/linux.data $(ROMFSIMG) > $(IMAGE) 改为
$(CROSS_COMPILE)ld -r -o $(ROOTDIR)/$(LINUXDIR)/romfs.o -b binary $(ROMFSIMG)
$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S $(ROOTDIR)/$(LINUXDIR)/linux $(RAMIMAGE) cp $(ROOTDIR)/$(LINUXDIR)/arch/armnommu/boot/zImage $(ROMIMAGE) 上面步骤基本OK了,运行编译过程
make menuconfig 选中芯片4510,库先用uc-libc
make dep 生成依赖关系
make lib_only 编译库
make user_only 编译选中的用户程序
make romfs 生成romfs文件目录
make image 第一次编译会有错误提示,但先生成了romfs.img,和romfs.o
make 编译内核并与romfs.o连接成可运行的image.ram、image.rom 注意在 make user_only时如果因boa出错可先不选它,或选用户程序时不选old_password 生成的image.ram文件可以下载到4510板上运行,注意它的波特率是19200。
romfs.img,image.rom,image.ram(1)
Q: 编译之后在 images 目录下生成的那些文件都是什么意思
A: 对于一些可能出现的文件,这里做一个解释:
romfs.img: 这个文件是通过 tools/romfs-inst.sh 脚本,生成 romfs 目录及其
下面的文件,然后通过 genromfs 程序打包成一个文件 romfs.img
romfs.img 既可以放在 flash 中,也可以放在 ram 中,但需要在
编译内核时在 driver/block/blkmem.c 里指定地址,以便内核找到.
linux.text: 编译后的内核中 text 段,一般放在 flash 中,只读.
linux.data: 编译后的内核中 data,init 段,一般放在 sdram 中,读写.
image.bin: 上面三个文件顺序连接而生成的 = linux.text + linux.data + romfs.img
有的时候还会得到:
image.rom: 通常 image.rom 的文件大小要比 image.ram 小很多,这是因为 image.rom 是
一个压缩过的内核,前面加上一个小的解压程序负责把内核解压后搬到指定位置.
这个文件可以直接烧进 flash 中,当然也可以在 ram 中运行.这个文件实际上
就是通常 linux 生成的 zImage 文件.
image.ram: 这个内核没有压缩过,而且必须在 ram 里运行.所以需要通过板子上的 bootloader
将它下载到指定位置后开始执行.相对 image.rom 而言,从 ram 里启动内核,则
代码段和数据段都在 ram 里面.这个文件是编译生成的 elf 格式的linux内核,通过
arm-elf-objcopy 工具生成的二进制映像文件.
可以这样形象的说: image.rom = gunzip program + gzipped(image.ram)
boot.rom: 其实就是 uClinux 编译完成后的文件 images/romfs.img,可以看一下它就是一个
连接.
//////////////////////////////////////////////////////////////////////////////////
uClinux操作系统移植
来源:BIPLIP.com 作者:沈沙 (复旦大学电子工程系沈沙,欢迎转载,转载时请保留此行作者信息) l uClinux简介
l 硬件体系结构简介
l 编译环境和编译工具。
l uClinux启动过程
l 系统源码的修改 1. uClinux简介 uClinux这个英文单词中u表示Micro,小的意思,C表示Control,控制的意思,所以uClinux就是Micro-Control-Linux,字面上的理解就是"针对微控制领域而设计的Linux系统"。 uclinux是一个源码开放的操作系统,面向没有MMU(Memory Management Unit)的硬件平台。它是linux的一个变种,主要的区别在于两者的内存管理机制和进程调度管理机制,同时为了适应嵌入式应用的需求,它的采用了romfs文件系统,并对linux上的c语言库glibc做了简化。 2. 硬件体系结构简介 运行uClinux的硬件平台主要包括如下几个部分:cpu(ARMv4指令集兼容)、uart、memory controller、定时器、flash存储器,sdram存储器,中断控制器和DMA。
3. 编译环境和编译工具 uclinux操作系统源码绝大部分是用c语言开发的,有一些与硬件直接相关的代码则用特定于某一CPU体系结构的汇编来实现。这些源码只能用GNU的gcc编译工具来进行编译、链接。 GNU gcc可以运行于Linux/Unix操作系统上。如果要在Windows平台上运行gcc,则必须安装Cygwin。Cygwin可以在Windows中安装一个linux的运行环境,这样就可以在windows下运行原本只能在linux下运行的程序。 为了在PC上编译得到运行于目标CPU上的操作系统内核,还必须安装一个合适的交叉编译器。Gcc 提供了现成的针对MIPS、ARM、M68K、Sharc、PowerPC的交叉编译器。如果没有现成的交叉编译器,则需要自行设计。GNU网站提供了一些如何开发新的交叉编译器的文章。开发一个新的编译器,一般需要如下几个步骤: (1)、编写机器描述脚本。采用gcc的RTL(Register Tansfer Language)语言描述针对某一CPU体系结构的机器指令与寻址方式、CPU浮点处理方式、endianess、c语言中各种数据类型的位宽、寄存器的个数和使用规则、堆栈和函数调用规则等体系结构的细节。
(2)、设计代码生成器。Gcc在对c语言源文件进行了词法和语法分析后,将产生一种中间格式文件(intermediate representation)。为了把这种中间格式文件转化为针对具体CPU体系结构的机器码,需要自行设计一个代码生成器。
(3)、设计汇编器
(4)、设计链接器
4. uClinux启动过程 uClinux系统的启动可以分为两个步骤:
(1). 运行bootloader初始化程序 SRAM、SDRAM等存储设备属于挥发性的存储器,掉电以后其中的内容就会全部丢失,所以必须把操作系统的内核镜像存放在Flash等不挥发性存储介质上。但是操作系统在运行时,需要动态的创建一些如数据段、堆栈、页表(针对使用虚拟地址的操作系统)等内容,所以需要在RAM中运行操作系统。因此,就需要一个引导程序把操作系统的内核镜像从Flash存储器拷贝到RAM中,然后再从RAM中执行操作系统的内核。Bootloader就是可以完成这样一种功能的程序。 从本质上来讲,bootloader不属于操作系统内核。它采用汇编语言编写,因此针对不同的cpu体系结构,这一部分代码不具有可移植性。在移植操作系统时,这部分代码必须加以改写。 具体来讲,bootloader在系统启动时主要完成以下几项工作:
· 将操作系统内核从flash拷贝到sdram中,如果是压缩格式的内核,还要将之解压缩。
· 改写系统的memory map,原先flash起始地址映射为0地址,这时需要将RAM的起始地址映射为0。
· 设置堆栈指针并将bss段清零。将来执行c语言程序和调用子函数时要用到。
· 改变pc值,使得cpu开始执行真正的操作系统内核。
(2) 运行操作系统内核
bootloader程序执行完上述的各项工作后,通过一条跳转指令,转而执行ini目录下c语言源文件main.c中的函数start_kernel()。因为在此之前bootloader已经创建好一个初始化环境,
c函数可以开始执行了。整个操作系统内核的初始化工作从这里才算是真正开始。这个函数的长度比较短,代码如下: /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
asmlinkage void __init start_kernel(void)
{
char * command_line;
unsigned long mempages;
extern char saved_command_line[]; /*
* Interrupts are still disabled. Do necessary setups, then
* enable them
*/
lock_kernel();
printk(linux_banner);
setup_arch(&command_line);
printk("Kernel command line: %sn", saved_command_line);
parse_options(command_line);
trap_init();
init_IRQ();
sched_init();
softirq_init();
time_init(); /*
* 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.
*/
console_init();
#ifdef CONFIG_MODULES
init_modules();
#endif
if (prof_shift) {
unsigned int size;
/* only text is profiled */
prof_len = (unsigned long) &_etext - (unsigned long) &_stext;
prof_len >>= prof_shift; size = prof_len * sizeof(unsigned int) + PAGE_SIZE-1;
prof_buffer = (unsigned int *) alloc_bootmem(size);
} kmem_cache_init();
sti();
calibrate_delay();
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && !initrd_below_start_ok &&
initrd_start < min_low_pfn << PAGE_SHIFT) {
printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
"disabling it.n",initrd_start,min_low_pfn << PAGE_SHIFT);
initrd_start = 0;
}
#endif
mem_init();
kmem_cache_sizes_init();
pgtable_cache_init(); mempages = num_physpages; fork_init(mempages);
proc_caches_init();
vfs_caches_init(mempages);
buffer_init(mempages);
page_cache_init(mempages);
#if defined(CONFIG_ARCH_S390)
ccwcache_init();
#endif
signals_init();
#ifdef CONFIG_PROC_FS
proc_root_init();
#endif
#if defined(CONFIG_SYSVIPC)
ipc_init();
#endif
check_bugs();
printk("POSIX conformance testing by UNIFIXn"); /*
* We count on the initial thread going ok
* Like idlers init is an unlocked kernel thread, which will
* make syscalls (and thus be locked).
*/
smp_init();
rest_init();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
5. 系统源码的修改 移植一个操作系统到新的硬件平台,比较好的办法是寻找一个架构相近并且已经做好操作系统移植的硬件平台。然后,对原先的操作系统做一定修改。系统源码修改的工作量取决于自行设计的硬件平台与现有的硬件平台之间差异程度。此设计中的硬件平台与三星4510芯片较为接近,并且也可以下载到针对4510b的uclinux系统源码。所以可以从此源码入手,根据我们的硬件平台与4510b的不同之处,在源码中找到相应的文件并加以修改。下面介绍如何修改系统源码。
需要修改的系统源码主要有如下几处:
(1) bootloader相关代码。此代码位于uClinuxlinux-2.4.xarcharmnommubootcompressed目录下名为head.s的文件中。此处程序用汇编语言实现,需要修改的地方主要是设置memory map的代码,与memory controller的硬件实现相关。
(2) UART相关代码。UART相关代码位于uClinuxlinux-2.4.xdriverschar目录下的serial.c
(3) 定时器相关代码。uClinux中有如下函数调用star_kernel()->time_init()->setup_timer(),需要修改setup_timer()函数中的相关代码。
(4) 中断控制器相关。uClinuxlinux-2.4.xarcharmnommuirq.c 除了上述的代码,还有其他多处需要修改。在修改源代码时,可按照uclinux启动和执行顺序依次修改整个平台。熟悉linux内核源码结构对操作系统移植有很大帮助。
Big Endian and Little Endian
谈到字节序的问题,必然牵涉到两大CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列采用big endian方式存储数据,而x86系列则采用little endian方式存储数据。那么究竟什么是big endian,什么又是little endian呢? 其实big endian是指低地址存放最高有效字节(MSB),而little endian则是低地址存放最低有效字节(LSB)。
用文字说明可能比较抽象,下面用图像加以说明。比如数字0x12345678在两种不同字节序CPU中的存储顺序如下所示: Big Endian 低地址 高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 12 | 34 | 56 | 78 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Little Endian 低地址 高地址
----------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 78 | 56 | 34 | 12 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 从上面两图可以看出,采用big endian方式存储数据是符合我们人类的思维习惯的。而little endian,!@#$%^&*,见鬼去吧 -_-||| 为什么要注意字节序的问题呢?你可能这么问。当然,如果你写的程序只在单机环境下面运行,并且不和别人的程序打交道,那么你完全可以忽略字节序的存在。但是,如果你的程序要跟别人的程序产生交互呢?在这里我想说说两种语言。C/C++语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的,而JAVA编写的程序则唯一采用big endian方式来存储数据。试想,如果你用C/C++语言在x86平台下编写的程序跟别人的JAVA程序互通时会产生什么结果?就拿上面的0x12345678来说,你的程序传递给别人的一个数据,将指向0x12345678的指针传给了JAVA程序,由于JAVA采取big endian方式存储数据,很自然的它会将你的数据翻译为0x78563412。什么?竟然变成另外一个数字了?是的,就是这种后果。因此,在你的C程序传给JAVA程序之前有必要进行字节序的转换工作。 无独有偶,所有网络协议也都是采用big endian的方式来传输数据的。所以有时我们也会把big endian方式称之为网络字节序。当两台采用不同字节序的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。ANSI C中提供了下面四个转换字节序的宏。
RAM文件和ROM文件.ROM文件的属性是只读文件,不能写入和删除,但能够改名.改名是非常有用的,因为可以通过把一个ROM文件名改名后,把新的同名的RAM文件下装下来,取代旧的ROM文件,从而避免重新固化新的ROM文件的麻烦.但清内存恢复到出厂设置后,ROM文件名又将恢复到固化时的文件名,必须重新运行"文件管理"对其进行改名. RAM文件和ROM文件.从外观上看RAM文件与ROM文件是没有区别的.ROM文件是只读文件,不能写入和删除,但能够改名.改名是非常有用的,它使用户可以通过改名而用RAM文件取代旧的ROM文件,ROM文件是固化到EPROM里面去的,是永久性的.ROM文件的组成是在生成ROM映象文件时确定的.RAM文件的使用则非常灵活,与PC机上放在硬盘上的文件是一样的,而且存取速度非常之快.建议用户应用程序里的关键数据应尽量放在RAM文件里,而不要结合到程序里去(如放在数组里或放在动态申请的内存里等);因为放在RAM文件里的数据的存取速度与结合到程序里去的速度通常是差不多的,而放到RAM文件里的文件能更好地利用RAM空间和保护用户数据.放到RAM文件里的数据是独立于用户程序而存在的,一旦用户程序出现死机现象或其它不可预料的情形,可以通过重新启动而把机器恢复正常,而RAM文件则依然存在.
系统配置文件和自动批命令文件 概念:.rom 是烧写到 Flash,.ram 是下载到 SDRAM 映象就是映象...
一般映象文件能装载引导
没什么好说的,就好比,win98安装盘里有引导,如果你直接把安装盘拷贝在硬盘里在刻出来一定没用引导,但如果做成iso在刻出来就有引导,明白... 什么叫映像文件?
建立映像文件的好处是什么?
---- 映像文件是将资料和程序结合而成的文件,它将来源资料经过格式转换后在硬盘上存成与目的光盘内容完全一样的文件,然后我们可以将这个文件以一比一对应的方式刻入光盘中。在制作映像文件之前建议先做硬盘的资料重整与磁盘扫描,除此之外,由于一个映像文件的大小相当于刻入光盘的全部内容,所以一定要预留超过这个容量的硬盘空间来存储这个映像文件。 ---- 既然可以用直接方式向光盘写入文件,为什么还要如此麻烦地制作一个映像文件呢?要知道制作映像文件同样是个耗费时间的过程,这样做是否多此一举?其实不然,在您正式刻录之前,制作一个映像文件有许多好处。首先,建立映像文件能提高刻录成功的几率,因为刻录资料的过程包括读取文件、转换格式和实际写入等阶段,在正式刻录过程中只要一个阶段出现问题都会造成刻录失败。而制作映像文件则能很好地控制这些过程,增加刻录成功的机会;其次,制作映像文件最大的好处在于可方便地进行同样资料的大量复制,能节省不少直接写入方式的重复时间;另外,建立映像文件进行刻录时不会产生许多小容量的文件,这样在刻录过程中能避免Buffer underrun(缓存不足)的产生。而通过常用的刻录软件Adaptec Easy CD Creator制作映像文件和利用映像文件刻录光盘的步骤也非常简单,希望刻录过程更加顺畅的用户不妨在刻录之前尽量多走这一步。
相关阅读 更多 +