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

***************************************************************************************************************************
作者: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 的调试函数。

 

你可能感兴趣的:(linux下machine_desc结构体中的phys_io与io_pg_offst 的作用及使用方法)