您的位置:首页 > 运维架构 > Linux

linux下machine_desc结构体中的phys_io与io_pg_offst 的作用及使用方法

2012-02-12 12:24 369 查看
***************************************************************************************************************************

作者:EasyWave 时间:2012.02.12

类别:linux驱动开发 声明:转载,请保留链接

***************************************************************************************************************************

1. phys_io 与 io_pg_offst

我们在移植BSP的时候需要填充 machine_desc 结构体,其中有两个字段 phys_io 和 io_pg_offst,如下红色加粗部分:

MACHINE_START(W90P950EVB, "W90P950EVB")

.phys_io = W90X900_PA_UART,

.io_pg_offst = (((u32)W90X900_VA_UART) >> 18) & 0xfffc,

.boot_params = 0x100,

.map_io = nuc950evb_map_io,

.init_irq = nuc900_init_irq,

.init_machine = nuc950evb_init,

.timer = &nuc900_timer,

MACHINE_END

在linux2.6.38中已经没有phys_io与io_pg_offs这两个变量,后面的文章会分析这个问题,现在就来分析linux2.6.35中在machine_desc结构体中,有关phys_io和io_pg_offst变量的作用以及使用方法,在介绍phys_io和io_pg_offst变量的作用之前,我们先来熟悉一些machine_desc这结构体:

struct machine_desc {
/*
* Note! The first four elements are used
* by assembler code in head.S, head-common.S
*/
unsigned int  nr;  /* architecture number */
unsigned int  phys_io; /* start of physical io */
unsigned int  io_pg_offst; /* byte offset for io
* page tabe entry */

const char  *name;  /* architecture name */
unsigned long  boot_params; /* tagged list  */

unsigned int  video_start; /* start of video RAM */
unsigned int  video_end; /* end of video RAM */

unsigned int  reserve_lp0 :1; /* never has lp0 */
unsigned int  reserve_lp1 :1; /* never has lp1 */
unsigned int  reserve_lp2 :1; /* never has lp2 */
unsigned int  soft_reboot :1; /* soft reboot  */
void   (*fixup)(struct machine_desc *,
struct tag *, char **,
struct meminfo *);
void   (*map_io)(void);/* IO mapping function */
void   (*init_irq)(void);
struct sys_timer *timer;  /* system tick timer */
void   (*init_machine)(void);
};

行7和行8定义了这两个变量,phys_io:物理IO的起始地址,io_pg_offst:IO页表的偏移字节的地址(MMU页表)。phys_io 用来保存 UART 的物理地址,io_pg_offst 用来保存 UART 的内核空间虚拟地址。两者的映射关系在 arch/arm/kernel/head.S 中建立。这样,在 kernel 没有初始化完 MMU 时,就可以通过写 io_pg_offst 向
UART 打印调试信息。这主要在 low level 的调试函数中使用,比如 printascii。

2. phys_io 与 io_pg_offst 的映射关系如何建立

现在可以进入arch\arm\kernel\head.S中分析这两个变量的具体调用情况:

/*
*  linux/arch/arm/kernel/head.S
*
*  Copyright (C) 1994-2002 Russell King
*  Copyright (c) 2003 ARM Limited
*  All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*  Kernel startup code for all 32-bit CPUs
*/
............
............
............
/*
* Kernel startup entry point.
* ---------------------------
*
* This is normally called from the decompressor code.  The requirements
* are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
* r1 = machine nr, r2 = atags pointer.
*
* This code is mostly position independent, so if you link the kernel at
* 0xc0008000, you call this at __pa(0xc0008000).
*
* See linux/arch/arm/tools/mach-types for the complete list of machine
* numbers for r1.
*
* We're trying to keep crap to a minimum; DO NOT add any machine specific
* crap here - that's what the boot loader (or in extreme, well justified
* circumstances, zImage) is for.
*/
__HEAD
ENTRY(stext)
setmode	PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode
@ and irqs disabled
mrc	p15, 0, r9, c0, c0		@ get processor id
bl	__lookup_processor_type		@ r5=procinfo r9=cpuid
movs	r10, r5				@ invalid processor (r5=0)?
beq	__error_p			@ yes, error 'p'
bl	__lookup_machine_type		@ r5=machinfo
movs	r8, r5				@ invalid machine (r5=0)?
beq	__error_a			@ yes, error 'a'
bl	__vet_atags
bl	__create_page_tables

/*
* The following calls CPU specific code in a position independent
* manner.  See arch/arm/mm/proc-*.S for details.  r10 = base of
* xxx_proc_info structure selected by __lookup_machine_type
* above.  On return, the CPU will be ready for the MMU to be
* turned on, and r0 will hold the CPU control register value.
*/
ldr	r13, __switch_data		@ address to jump to after
@ mmu has been enabled
adr	lr, BSYM(__enable_mmu)		@ return (PIC) address
ARM(	add	pc, r10, #PROCINFO_INITFUNC	)
THUMB(	add	r12, r10, #PROCINFO_INITFUNC	)
THUMB(	mov	pc, r12				)
ENDPROC(stext)
.....
.....
.....
/*
* Setup the initial page tables.  We only setup the barest
* amount which are required to get the kernel running, which
* generally means mapping in the kernel code.
*
* r8  = machinfo
* r9  = cpuid
* r10 = procinfo
*
* Returns:
*  r0, r3, r6, r7 corrupted
*  r4 = physical page table address
*/
__create_page_tables:
pgtbl	r4				@ page table address

/*
* Clear the 16K level 1 swapper page table
*/
mov	r0, r4
mov	r3, #0
add	r6, r0, #0x4000
1:	str	r3, [r0], #4
str	r3, [r0], #4
str	r3, [r0], #4
str	r3, [r0], #4
teq	r0, r6
bne	1b

ldr	r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags

/*
* Create identity mapping for first MB of kernel to
* cater for the MMU enable.  This identity mapping
* will be removed by paging_init().  We use our current program
* counter to determine corresponding section base address.
*/
mov	r6, pc
mov	r6, r6, lsr #20			@ start of kernel section
orr	r3, r7, r6, lsl #20		@ flags + kernel base
str	r3, [r4, r6, lsl #2]		@ identity mapping

/*
* Now setup the pagetables for our kernel direct
* mapped region.
*/
add	r0, r4,  #(KERNEL_START & 0xff000000) >> 18
str	r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!
ldr	r6, =(KERNEL_END - 1)
add	r0, r0, #4
add	r6, r4, r6, lsr #18
1:	cmp	r0, r6
add	r3, r3, #1 << 20
strls	r3, [r0], #4
bls	1b

#ifdef CONFIG_XIP_KERNEL
/*
* Map some ram to cover our .data and .bss areas.
*/
orr	r3, r7, #(KERNEL_RAM_PADDR & 0xff000000)
.if	(KERNEL_RAM_PADDR & 0x00f00000)
orr	r3, r3, #(KERNEL_RAM_PADDR & 0x00f00000)
.endif
add	r0, r4,  #(KERNEL_RAM_VADDR & 0xff000000) >> 18
str	r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!
ldr	r6, =(_end - 1)
add	r0, r0, #4
add	r6, r4, r6, lsr #18
1:	cmp	r0, r6
add	r3, r3, #1 << 20
strls	r3, [r0], #4
bls	1b
#endif

/*
* Then map first 1MB of ram in case it contains our boot params.
*/
add	r0, r4, #PAGE_OFFSET >> 18
orr	r6, r7, #(PHYS_OFFSET & 0xff000000)
.if	(PHYS_OFFSET & 0x00f00000)
orr	r6, r6, #(PHYS_OFFSET & 0x00f00000)
.endif
str	r6, [r0]

#ifdef CONFIG_DEBUG_LL
ldr	r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
/*
* Map in IO space for serial debugging.
* This allows debug messages to be output
* via a serial console before paging_init.
*/
ldr	r3, [r8, #MACHINFO_PGOFFIO]
add	r0, r4, r3
rsb	r3, r3, #0x4000			@ PTRS_PER_PGD*sizeof(long)
cmp	r3, #0x0800			@ limit to 512MB
movhi	r3, #0x0800
add	r6, r0, r3
ldr	r3, [r8, #MACHINFO_PHYSIO]
orr	r3, r3, r7
1:	str	r3, [r0], #4
add	r3, r3, #1 << 20
teq	r0, r6
bne	1b
#if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)
/*
* If we're using the NetWinder or CATS, we also need to map
* in the 16550-type serial port for the debug messages
*/
add	r0, r4, #0xff000000 >> 18
orr	r3, r7, #0x7c000000
str	r3, [r0]
#endif
#ifdef CONFIG_ARCH_RPC
/*
* Map in screen at 0x02000000 & SCREEN2_BASE
* Similar reasons here - for debug.  This is
* only for Acorn RiscPC architectures.
*/
add	r0, r4, #0x02000000 >> 18
orr	r3, r7, #0x02000000
str	r3, [r0]
add	r0, r4, #0xd8000000 >> 18
str	r3, [r0]
#endif
#endif
mov	pc, lr
ENDPROC(__create_page_tables)

47行中我们可以看到:bl __create_page_tables 就进入到了create_page_tables函数中去了,这个函数在上面的79行,将151行开始的代码拿出来单独分析:

#ifdef CONFIG_DEBUG_LL

ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags

/*

* Map in IO space for serial debugging.

* This allows debug messages to be output

* via a serial console before paging_init.

*/

ldr r3, [r8, #MACHINFO_PGOFFIO]

add r0, r4, r3

rsb r3, r3, #0x4000 @ PTRS_PER_PGD*sizeof(long)

cmp r3, #0x0800 @ limit to 512MB

movhi r3, #0x0800

add r6, r0, r3

ldr r3, [r8, #MACHINFO_PHYSIO]

orr r3, r3, r7

1: str r3, [r0], #4 //这个循环把 phys_io 填充到 io_pg_offst 对应的 MMU 表项中

add r3, r3, #1 << 20

teq r0, r6

bne 1b

#if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)

/*

* If we're using the NetWinder or CATS, we also need to map

* in the 16550-type serial port for the debug messages

*/

add r0, r4, #0xff000000 >> 18

orr r3, r7, #0x7c000000

str r3, [r0]

#endif

#ifdef CONFIG_ARCH_RPC

/*

* Map in screen at 0x02000000 & SCREEN2_BASE

* Similar reasons here - for debug. This is

* only for Acorn RiscPC architectures.

*/

add r0, r4, #0x02000000 >> 18

orr r3, r7, #0x02000000

str r3, [r0]

add r0, r4, #0xd8000000 >> 18

str r3, [r0]

#endif

#endif


上面蓝色加粗部分的代码是在哪里定义的呢?它是在arch\arm\kernel\asm-offsets.c定义的,请看下面加粗的代码:

int main(void)

{

DEFINE(TSK_ACTIVE_MM, offsetof(struct task_struct, active_mm));

BLANK();

DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));

DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));

........

........

DEFINE(SYS_ERROR0, 0x9f0000);

BLANK();

DEFINE(SIZEOF_MACHINE_DESC, sizeof(struct machine_desc));

DEFINE(MACHINFO_TYPE, offsetof(struct machine_desc, nr));

DEFINE(MACHINFO_NAME, offsetof(struct machine_desc, name));

DEFINE(MACHINFO_PHYSIO, offsetof(struct machine_desc, phys_io));

DEFINE(MACHINFO_PGOFFIO, offsetof(struct machine_desc, io_pg_offst));

BLANK();

........

.........

return 0;

}

通过以上的DEFINE宏定义取出phys_io与io_pg_offst分别赋给了MACHINE_PHYSIO和MACHINE_PGOFFIO,这样, phys_io 和 io_pg_offst 就建立了映射关系。

3. printascii 与 uart

printascii 函数调用了一个 汇编宏 addruart。 这个宏在 arch/arm/mach-XXX/include/mach/debug-macro.S 中定义。它的代码一般是这种形式:

.macro addruart,rx

@ see if the MMU is enabled and select appropriate base address

mrc p15, 0, \rx, c1, c0

tst \rx, #1

ldreq \rx, =SUART_BASE_PA

ldrne \rx, =SUART_BASE_UA

.endm

显然,这里用到了在 head.S 中建立的映射关系。这个函数有些芯片并没有去实现。这个函数只用于low level 的调试函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: