linux下lk和kernel层通信方式[2]
U-Boot与Linux内核的交互
说明:本文所使用的U-Boot的版本是1.1.6,平台是S3C2440。
目录
1.1标记列表
二、设置标记存放的地址
2.1相关的结构体定义
2.2标记存放地址的设定
三、标记的设置
3.1设置标记ATAG_CORE
3.2设置内存标记ATAG_MEM
3.3设置命令行标记ATAG_CMDLINE
3.4设置ATAG_NONE
一、简介
U-Boot与Linux内核的交互是单向的,U-Boot将各类参数传递给内核。由于他们不能同时运行,传递办法只能有一个个:U-Boot将参数放在某个约定的地方之后,在启动内核,内核启动后从这个地方获得参数。
1.1标记列表
除了约定好参数存放的地方外,还要规定参数的结构。Linux2.4.x以后的内核都以标记列表(tagged list)的形式来传递参数。标记就是一种数据结构;标记列表就是挨着存放的多个标记。标记列表以标记ATAG_CORE开始,以ATAGE_NONE结束。
标记的数据结构为tag,它是偶一个tag_header结构和一个联合体(union)组成。tag_header结构体表示标记的类型及长度,比如是表示内存还是表示命令行参数等。对于不同类型的标记使用不同的联合体,比如表示内存=时使用tag_men32,表示命令行时使用tag_cmdline。其定定义在include/asm-arm/setup.c文件中。
/*
* The new way of passing information: a list of tagged entries
*/ /* The list ends with an ATAG_NONE node. */
#define ATAG_NONE 0x00000000 struct tag_header {
u32 size;
u32 tag;
}; /* The list must start with an ATAG_CORE node */
#define ATAG_CORE 0x54410001 struct tag_core {
u32 flags; /* bit 0 = read-only */
u32 pagesize;
u32 rootdev;
}; /* it is allowed to have multiple ATAG_MEM nodes */
#define ATAG_MEM 0x54410002 struct tag_mem32 {
u32 size;
u32 start; /* physical start address */
}; /* VGA text type displays */
#define ATAG_VIDEOTEXT 0x54410003 struct tag_videotext {
u8 x;
u8 y;
u16 video_page;
u8 video_mode;
u8 video_cols;
u16 video_ega_bx;
u8 video_lines;
u8 video_isvga;
u16 video_points;
}; /* describes how the ramdisk will be used in kernel */
#define ATAG_RAMDISK 0x54410004 struct tag_ramdisk {
u32 flags; /* bit 0 = load, bit 1 = prompt */
u32 size; /* decompressed ramdisk size in _kilo_ bytes */
u32 start; /* starting block of floppy-based RAM disk image */
}; /* describes where the compressed ramdisk image lives (virtual address) */
/*
* this one accidentally used virtual addresses - as such,
* its depreciated.
*/
#define ATAG_INITRD 0x54410005 /* describes where the compressed ramdisk image lives (physical address) */
#define ATAG_INITRD2 0x54420005 struct tag_initrd {
u32 start; /* physical start address */
u32 size; /* size of compressed ramdisk image in bytes */
}; /* board serial number. "64 bits should be enough for everybody" */
#define ATAG_SERIAL 0x54410006 struct tag_serialnr {
u32 low;
u32 high;
}; /* board revision */
#define ATAG_REVISION 0x54410007 struct tag_revision {
u32 rev;
}; /* initial values for vesafb-type framebuffers. see struct screen_info
* in include/linux/tty.h
*/
#define ATAG_VIDEOLFB 0x54410008 struct tag_videolfb {
u16 lfb_width;
u16 lfb_height;
u16 lfb_depth;
u16 lfb_linelength;
u32 lfb_base;
u32 lfb_size;
u8 red_size;
u8 red_pos;
u8 green_size;
u8 green_pos;
u8 blue_size;
u8 blue_pos;
u8 rsvd_size;
u8 rsvd_pos;
}; /* command line: \0 terminated string */
#define ATAG_CMDLINE 0x54410009 struct tag_cmdline {
char cmdline[]; /* this is the minimum size */
}; /* acorn RiscPC specific information */
#define ATAG_ACORN 0x41000101 struct tag_acorn {
u32 memc_control_reg;
u32 vram_pages;
u8 sounddefault;
u8 adfsdrives;
}; /* footbridge memory clock, see arch/arm/mach-footbridge/arch.c */
#define ATAG_MEMCLK 0x41000402 struct tag_memclk {
u32 fmemclk;
}; struct tag {
struct tag_header hdr;
union {
struct tag_core core;
struct tag_mem32 mem;
struct tag_videotext videotext;
struct tag_ramdisk ramdisk;
struct tag_initrd initrd;
struct tag_serialnr serialnr;
struct tag_revision revision;
struct tag_videolfb videolfb;
struct tag_cmdline cmdline; /*
* Acorn specific
*/
struct tag_acorn acorn; /*
* DC21285 specific
*/
struct tag_memclk memclk;
} u;
}; #define tag_next(t)<span style="white-space:pre"> </span>((struct tag *)((u32 *)(t) + (t)->hdr.size))
#define tag_size(type)<span style="white-space:pre"> </span>((sizeof(struct tag_header) + sizeof(struct type)) >> 2) //???
二、设置标记存放的地址
2.1相关的结构体定义
结构体bd中保存了标记存放的地址。bd结构体是gd结构体的一项,我们先看gd结构体,其定义在include/asm-arm/global_data.h文件中:
typedef struct global_data {
bd_t *bd;//开发板相关参数 ,结构体变量,参考u-boot.h
unsigned long flags;//指示标志,如设备已经初始化标志等
unsigned long baudrate;//串行口通讯速率
unsigned long have_console;
/* serial_init() was called 如果执行了该函数,则设置为1 */
unsigned long reloc_off;
/*
*Relocation Offset 重定位偏移,就是实际定向的位置与编译连接时指定的位置之差,一般为0
*/
unsigned long env_addr; /* 环境参数地址*/
unsigned long env_valid; /* 环境参数CRC检验有效标志*/
unsigned long fb_base; /*帧缓冲区基地址*/
#ifdef CONFIG_VFD
unsigned char vfd_type; /* 显示类型*/
#endif
#if 0
unsigned long cpu_clk; /*cpu时钟*/
unsigned long bus_clk; //总线时钟
unsigned long ram_size; /* RAM size */
unsigned long reset_status; /* reset status register at boot */
#endif
void **jt; /* jump table 跳转表,用来登记"函数调用地址"*/
} gd_t;
接来下我们来看一下bd结构体,这个结构体定义在include/asm-arm/u-boot.h文件中:
typedef struct bd_info {
int bi_baudrate; /* 串口波特率*/
unsigned long bi_ip_addr; /* IP 地址*/
unsigned char bi_enetaddr[]; /* MAC地址*/
struct environment_s *bi_env;
ulong bi_arch_number; /* 板子的id*/
ulong bi_boot_params; /* 启动参数*/
struct /* RAM 配置*/
{
ulong start;
ulong size;
}bi_dram[CONFIG_NR_DRAM_BANKS];
#ifdef CONFIG_HAS_ETH1
/* second onboard ethernet port */
unsigned char bi_enet1addr[];
#endif
} bd_t;
2.2标记存放地址的设定
在board/smdk2410/smdk2410.c的board_init 函数设置了bi_boot_params 参数:
int board_init (void)
{
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();//获取时钟和电源配置寄存器的第一个寄存器的地址,寄存器的地上是连续的
S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();//获取GPIO配置寄存器的第一个寄存器的地址 /* to reduce PLL lock time, adjust the LOCKTIME register */
clk_power->LOCKTIME = 0xFFFFFF; /* configure MPLL */
clk_power->MPLLCON = ((M_MDIV << ) + (M_PDIV << ) + M_SDIV); /* some delay between MPLL and UPLL */
delay (); /* configure UPLL */
clk_power->UPLLCON = ((U_M_MDIV << ) + (U_M_PDIV << ) + U_M_SDIV); /* some delay between MPLL and UPLL */
delay (); /* set up the I/O ports */
gpio->GPACON = 0x007FFFFF;
gpio->GPBCON = 0x00044555;
gpio->GPBUP = 0x000007FF;
gpio->GPCCON = 0xAAAAAAAA;
gpio->GPCUP = 0x0000FFFF;
gpio->GPDCON = 0xAAAAAAAA;
gpio->GPDUP = 0x0000FFFF;
gpio->GPECON = 0xAAAAAAAA;
gpio->GPEUP = 0x0000FFFF;
gpio->GPFCON = 0x000055AA;
gpio->GPFUP = 0x000000FF;
gpio->GPGCON = 0xFF95FFBA;
gpio->GPGUP = 0x0000FFFF;
gpio->GPHCON = 0x002AFAAA;
gpio->GPHUP = 0x000007FF; /* arch number of SMDK2410-Board */
gd->bd->bi_arch_number = MACH_TYPE_SMDK2410; /* adress of boot parameters */
gd->bd->bi_boot_params = 0x30000100; icache_enable(); //调用cpu/arm920t/cpu.c中的函数
dcache_enable(); return ;
}
三、标记的设置
U-Boot通过bootm命令引导Linux内核,bootm命令对吼调用do_bootm_linux函数来引导内核。在do_bootm_linux函数就设置了标记,该函数的定义在lib_arm/armlinux.c中:
void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[],
ulong addr, ulong *len_ptr, int verify)
{
ulong len = , checksum;
ulong initrd_start, initrd_end;
ulong data;
void (*theKernel)(int zero, int arch, uint params);
image_header_t *hdr = &header;
bd_t *bd = gd->bd; #ifdef CONFIG_CMDLINE_TAG
char *commandline = getenv ("bootargs");
#endif theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);
设置kernal加载地址 /*
* Check if there is an initrd image
*/
用户自定义了initrd之后需要加载进来,整个过程需要进行头部以及整个数据内部校,类似于内核的加载校验,这里省略了。
initial RAM disk Linux初始 RAM磁盘(initrd)是在系统引导过程中挂载的一个临时根文件系统,用来支持两阶段的引导过程。initrd文件中包含了各种可执行程序和驱动程序,它们可以用来挂载实际的根文件系统,然后再将这个 initrd RAM 磁盘卸载,并释放内存。在很多嵌入式Linux 系统中,initrd 就是最终的根文件系统。
if (argc >= ) {
SHOW_BOOT_PROGRESS (); addr = simple_strtoul (argv[], NULL, ); printf ("## Loading Ramdisk Image at %08lx ...\n", addr); /* Copy header so we can blank CRC field for re-calculation */
#ifdef CONFIG_HAS_DATAFLASH
if (addr_dataflash (addr)) {
read_dataflash (addr, sizeof (image_header_t),
(char *) &header);
} else
#endif
memcpy (&header, (char *) addr,
sizeof (image_header_t)); if (ntohl (hdr->ih_magic) != IH_MAGIC) {
printf ("Bad Magic Number\n");
SHOW_BOOT_PROGRESS (-);
do_reset (cmdtp, flag, argc, argv);
} data = (ulong) & header;
len = sizeof (image_header_t); checksum = ntohl (hdr->ih_hcrc);
hdr->ih_hcrc = ; if (crc32 (, (unsigned char *) data, len) != checksum) {
printf ("Bad Header Checksum\n");
SHOW_BOOT_PROGRESS (-);
do_reset (cmdtp, flag, argc, argv);
} SHOW_BOOT_PROGRESS (); print_image_hdr (hdr); data = addr + sizeof (image_header_t);
len = ntohl (hdr->ih_size); #ifdef CONFIG_HAS_DATAFLASH
if (addr_dataflash (addr)) {
read_dataflash (data, len, (char *) CFG_LOAD_ADDR);
data = CFG_LOAD_ADDR;
}
#endif if (verify) {
ulong csum = ; printf (" Verifying Checksum ... ");
csum = crc32 (, (unsigned char *) data, len);
if (csum != ntohl (hdr->ih_dcrc)) {
printf ("Bad Data CRC\n");
SHOW_BOOT_PROGRESS (-);
do_reset (cmdtp, flag, argc, argv);
}
printf ("OK\n");
} SHOW_BOOT_PROGRESS (); if ((hdr->ih_os != IH_OS_LINUX) ||
(hdr->ih_arch != IH_CPU_ARM) ||
(hdr->ih_type != IH_TYPE_RAMDISK)) {
printf ("No Linux ARM Ramdisk Image\n");
SHOW_BOOT_PROGRESS (-);
do_reset (cmdtp, flag, argc, argv);
} #if defined(CONFIG_B2) || defined(CONFIG_EVB4510) || defined(CONFIG_ARMADILLO)
/*
*we need to copy the ramdisk to SRAM to let Linux boot
*/
memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
data = ntohl(hdr->ih_load);
#endif /* CONFIG_B2 || CONFIG_EVB4510 */ /*
* Now check if we have a multifile image
*/
} else if ((hdr->ih_type == IH_TYPE_MULTI) && (len_ptr[])) {
ulong tail = ntohl (len_ptr[]) % ;
int i; SHOW_BOOT_PROGRESS (); /* skip kernel length and terminator */
data = (ulong) (&len_ptr[]);
/* skip any additional image length fields */
for (i = ; len_ptr[i]; ++i)
data += ;
/* add kernel length, and align */
data += ntohl (len_ptr[]);
if (tail) {
data += - tail;
} len = ntohl (len_ptr[]); } else {
/*
* no initrd image
*/
SHOW_BOOT_PROGRESS (); len = data = ;
} #ifdef DEBUG
if (!data) {
printf ("No initrd\n");
}
#endif if (data) {
initrd_start = data;
initrd_end = initrd_start + len;
} else {
initrd_start = ;
initrd_end = ;
} SHOW_BOOT_PROGRESS (); debug ("## Transferring control to Linux (at address %08lx) ...\n",
(ulong) theKernel); #if defined (CONFIG_SETUP_MEMORY_TAGS) || \
defined (CONFIG_CMDLINE_TAG) || \
defined (CONFIG_INITRD_TAG) || \
defined (CONFIG_SERIAL_TAG) || \
defined (CONFIG_REVISION_TAG) || \
defined (CONFIG_LCD) || \
defined (CONFIG_VFD)
setup_start_tag (bd);设置各种tag,用于传递参数给Linux
#ifdef CONFIG_SERIAL_TAG
setup_serial_tag (¶ms);
#endif
#ifdef CONFIG_REVISION_TAG
setup_revision_tag (¶ms);
#endif
#ifdef CONFIG_SETUP_MEMORY_TAGS
setup_memory_tags (bd);
#endif
#ifdef CONFIG_CMDLINE_TAG
setup_commandline_tag (bd, commandline);
#endif
#ifdef CONFIG_INITRD_TAG
if (initrd_start && initrd_end)
setup_initrd_tag (bd, initrd_start, initrd_end);
#endif
#if defined (CONFIG_VFD) || defined (CONFIG_LCD)
setup_videolfb_tag ((gd_t *) gd);
#endif
setup_end_tag (bd);
#endif /* we assume that the kernel is in place */
printf ("\nStarting kernel ...\n\n");打印信息 #ifdef CONFIG_USB_DEVICE
{
extern void udc_disconnect (void);
udc_disconnect ();
}
#endif cleanup_before_linux ();启动之前先做一些清理工作cpu/arm920t/cpu.c 调用内核需要传递的参数如下:
R0:必须为0
R1:机器类型ID,本机为ARM(bd->bi_arch_number)
R2:启动参数列表在内存中的位置(bd->bi_boot_params)
theKernel (, bd->bi_arch_number, bd->bi_boot_params);
}
3.1设置标记ATAG_CORE
标记列表以标记ATAG_CORE开始
static void setup_start_tag (bd_t *bd)
{
params = (struct tag *) bd->bi_boot_params; params->hdr.tag = ATAG_CORE;
params->hdr.size = tag_size (tag_core); params->u.core.flags = ;
params->u.core.pagesize = ;
params->u.core.rootdev = ; params = tag_next (params);//指向当前标记的末尾
}
3.2设置内存标记ATAG_MEM
在board/smdk2410/smdk2410.c的dram_init函数设置了bd的bi_dram结构体:
int dram_init (void)
{
gd->bd->bi_dram[].start = PHYS_SDRAM_1;
gd->bd->bi_dram[].size = PHYS_SDRAM_1_SIZE; return ;
}
下面是这边内存标记的结构体:
static void setup_memory_tags (bd_t *bd)
{
int i; for (i = ; i < CONFIG_NR_DRAM_BANKS; i++) {
params->hdr.tag = ATAG_MEM;
params->hdr.size = tag_size (tag_mem32); params->u.mem.start = bd->bi_dram[i].start;
params->u.mem.size = bd->bi_dram[i].size; params = tag_next (params);
}
}
3.3设置命令行标记ATAG_CMDLINE
命令行就是一个字符串,用来控制内核的一些行为。比如“root=/dev/mtdblock2 init=/linuxrc console=ttySAC0 ”表示根文件系统在MTD2分区上系统启动后执行的第一个程序为/linuxrc,控制台是ttySAC0 。
static void setup_commandline_tag (bd_t *bd, char *commandline)
{
char *p; if (!commandline)
return; /* eat leading white space */
for (p = commandline; *p == ' '; p++); /* skip non-existent command lines so the kernel will still
* use its default command line.
*/
if (*p == '\0')
return; params->hdr.tag = ATAG_CMDLINE;
params->hdr.size =
(sizeof (struct tag_header) + strlen (p) + + ) >> ; strcpy (params->u.cmdline.cmdline, p); params = tag_next (params);
}
3.4设置ATAG_NONE
标记列表以标记ATAG_NONE介绍。
linux下lk和kernel层通信方式[2]的更多相关文章
- Linux下的IPC几种通信方式
Linux下的IPC几种通信方式 管道(pipe):管道可用于具有亲缘关系的进程间的通信,是一种半双工的方式,数据只能单向流动,允许一个进程和另一个与它有公共祖先的进程之间进行通信. 命名管道(nam ...
- linux下GPIO的用户层操作(sysfs)
linux的GPIO通过sysfs为用户提供服务,下面是linux kernel里的说明文档,学习一下. GPIO Sysfs Interface for Userspace ============ ...
- Linux下VirtualBox出现kernel driver not installed的解决方法
今天安装好rhel-server-6.6-i386后,再安装VirtualBox成功,但是再VirtualBox中创建虚拟机的时候出现了“不能为xx虚拟机打开新任务” 并弹出如下的错误信息:
- Android HAL层与Linux Kernel层驱动开发简介
近日稍微对Android中的驱动开发做了一些简要的了解. HAL:Hardware Abstract Layer 硬件抽象层,由于Linux Kernel需要遵循GPL开源协议,硬件厂商为了保护自己硬 ...
- Linux/Android——input系统之 kernel层 与 frameworks层交互 (五)【转】
本文转载自:http://blog.csdn.net/jscese/article/details/42291149 之前的四篇博文记录的都是linux中的input体系相关的东西,最底层以我调试的u ...
- Linux下USB烧写uImage kernel
Linux下USB烧写uImage kernel 1.启动开发板,进入u-boot:(如果开发板中没有系统,可以通过用SD卡方式启动开发板进入) U-Boot 2011.06 (Mar 19 ...
- 【网络编程基础】Linux下进程通信方式(共享内存,管道,消息队列,Socket)
在网络课程中,有讲到Socket编程,对于tcp讲解的环节,为了加深理解,自己写了Linux下进程Socket通信,在学习的过程中,又接触到了其它的几种方式.记录一下. 管道通信(匿名,有名) 管道通 ...
- 【Linux】Linux下进程间的通信方式
本文内容: 1.进程通信的目的 2.介绍Linux下进程间的4种通信方式:管道,消息队列,共享内存,信号量 ps:套接字也可以用于进程间的通信,不过是不同物理机器上的进程通信,本章讨论是是同一台物理机 ...
- 【转】Linux下Fork与Exec使用
Linux下Fork与Exec使用 转自 Linux下Fork与Exec使用 一.引言 对于没有接触过Unix/Linux操作系统的人来说,fork是最难理解的概念之一:它执行一次却返回两个值.for ...
随机推荐
- BZOJ 4808 马 二分图最大独立集
题目应该就是最大独立集了吧,没什么了,平面图求最大独立集需要/2的, WQH说加直接+双向边考研过,结果真的过了,应该是匈牙利算法寻找的 时候更加快了吧.(方便找边) #include<cstd ...
- JS return false 与 return true
在大多数情况下,为事件处理函数返回false,可以防止默认的事件行为.例如,默认情况下点击一个<a>元素,页面会跳转到该元素href属性指定的页. Return False 就相当于终止符 ...
- poj 2891 模线性方程组求解
Strange Way to Express Integers Time Limit: 1000MS Memory Limit: 131072K Total Submissions: 8005 ...
- Linux服务器性能分汇总
工具使用: vmstat 查看cpu时间.内存.IO的情况. 参考:http://linuxcommand.org/man_pages/vmstat8.html 基本用法: [root@root ~] ...
- Hello SpringMVC
1. MVC框架能做哪些事情? 讲url映射到java类或者方法 封装用户提交的数据 处理请求-调用相关业务处理-封装相应数据 将相应数据进行渲染 jsp/html/freemaker等 ... 2. ...
- 漫话最小割 part1
codeforces 724D [n个城市每个城市有一个特产的产出,一个特产的最大需求.当i<j时,城市i可以运最多C个特产到j.求所有城市可以满足最大的需求和] [如果直接最大流建图显然会T. ...
- 迈出从3K到1W的重要一步——掌握设计模式
IT职场的小菜经常有这样的疑问: 为什么一个相似的功能,大牛一会儿就搞定,然后悠闲地品着下午茶逛淘宝:而自己加班加点搞到天亮还做不完. 为什么用户提出需求变更后,大牛只需潇洒地敲敲键盘,改改配置:而自 ...
- DELPHI方法注释的标准写法
/// <summary> /// 查询数据 /// </summary> /// <param name="accountno">帐套号< ...
- 【java】java base64编码与解码
参考地址:http://blog.csdn.net/zhou_kapenter/article/details/62890262 要求:JDK1.8+ 使用java原生工具类即可实现 [这里展示字符串 ...
- U盘容纳不了大于4G的文件比如ISO文件咋办?
格式化U盘成NTFS格式就行了,不这么做8,16,32G Upan都容纳不下来.