/*
 *  linux/arch/arm/kernel/head.S
 *
 *  Copyright (C) 1994-2002 Russell King
 *
 * 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
 */
#include <linux/config.h>
#include <linux/linkage.h>
#include <linux/init.h>

#include <asm/assembler.h>
#include <asm/domain.h>
#include <asm/mach-types.h>
#include <asm/procinfo.h>
#include <asm/ptrace.h>
#include <asm/constants.h>
#include <asm/system.h>

#define PROCINFO_MMUFLAGS 8
#define PROCINFO_INITFUNC 12

#define MACHINFO_PHYSRAM 4
#define MACHINFO_PHYSIO  8
#define MACHINFO_PGOFFIO 12

#ifndef CONFIG_XIP_KERNEL
/*
 * We place the page tables 16K below TEXTADDR.  Therefore, we must make sure
 * that TEXTADDR is correctly set.  Currently, we expect the least significant
 * 16 bits to be 0x8000, but we could probably relax this restriction to
 * TEXTADDR >= PAGE_OFFSET + 0x4000
 *
 * Note that swapper_pg_dir is the virtual address of the page tables, and
 * pgtbl gives us a position-independent reference to these tables.  We can
 * do this because stext == TEXTADDR
 */
#if (TEXTADDR & 0xffff) != 0x8000
#error TEXTADDR must start at 0xXXXX8000
#endif

 .globl swapper_pg_dir
 .equ swapper_pg_dir, TEXTADDR - 0x4000

 .macro pgtbl, rd, phys
 adr \rd, stext
 sub \rd, \rd, #0x4000
 .endm
#else
/*
 * XIP Kernel:
 *
 * We place the page tables 16K below DATAADDR.  Therefore, we must make sure
 * that DATAADDR is correctly set.  Currently, we expect the least significant
 * 16 bits to be 0x8000, but we could probably relax this restriction to
 * DATAADDR >= PAGE_OFFSET + 0x4000
 *
 * Note that pgtbl is meant to return the physical address of swapper_pg_dir.
 * We can't make it relative to the kernel position in this case since
 * the kernel can physically be anywhere.
 */
#if (DATAADDR & 0xffff) != 0x8000
#error DATAADDR must start at 0xXXXX8000
#endif

 .globl swapper_pg_dir
 .equ swapper_pg_dir, DATAADDR - 0x4000

 .macro pgtbl, rd, phys
 ldr \rd, =((DATAADDR - 0x4000) - VIRT_OFFSET)
 add \rd, \rd, \phys
 .endm
#endif

/*
 * 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.
 *
 * 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.
 */
 __INIT
 .type stext, %function
ENTRY(stext)     //<---------------------------------------------  여기부터 보자 99라인
 msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | MODE_SVC @ ensure svc mode 
      @ and irqs disabled//슈퍼바이저 모드 enable
 bl __lookup_processor_type  @ r5=procinfo r9=cpuid// 414 라인 으로점프!
 movs r10, r5    @ invalid processor (r5=0)?  //"P"라고 뜨면 프로세서 타입을 찾지 못한 것이다.
 moveq r0, #'p'   @ yes, error 'p'
 beq __error
 bl __lookup_machine_type  @ r5=machinfo  //다시 점프 462라인으로!
 movs r8, r5    @ invalid machine (r5=0)? // 타입이 맞지 않으면 r5번에 0 이들어가고 "LA"라는 에러 메세지 발동
 moveq r0, #'a'   @ yes, error 'a'
 beq __error
 bl __create_page_tables    //0xA000 / 4000 rhk 0xA000 / 8000 사이에 페이지 테이블을 생성하는 과정

 /*
  * 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, __enable_mmu  @ return (PIC) address
 add pc, r10, #PROCINFO_INITFUNC

 .type __switch_data, %object
__switch_data:
 .long __mmap_switched
 .long __data_loc   @ r4
 .long __data_start   @ r5
 .long __bss_start   @ r6
 .long _end    @ r7
 .long processor_id   @ r4
 .long __machine_arch_type  @ r5
 .long cr_alignment   @ r6
 .long init_thread_union+8192  @ sp

/*
 * The following fragment of code is executed with the MMU on, and uses
 * absolute addresses; this is not position independent.
 *
 *  r0  = cp#15 control register
 *  r1  = machine ID
 *  r9  = processor ID
 */
 .type __mmap_switched, %function
__mmap_switched:
 adr r3, __switch_data + 4

 ldmia r3!, {r4, r5, r6, r7}
 cmp r4, r5    @ Copy data segment if needed
1: cmpne r5, r6
 ldrne fp, [r4], #4
 strne fp, [r5], #4
 bne 1b

 mov fp, #0    @ Clear BSS (and zero fp)
1: cmp r6, r7
 strcc fp, [r6],#4
 bcc 1b

 ldmia r3, {r4, r5, r6, sp}
 str r9, [r4]   @ Save processor ID
 str r1, [r5]   @ Save machine type
 bic r4, r0, #CR_A   @ Clear 'A' bit
 stmia r6, {r0, r4}   @ Save control register values
 b start_kernel

 

/*
 * Setup common bits before finally enabling the MMU.  Essentially
 * this is just loading the page table pointer and domain access
 * registers.
 */
 .type __enable_mmu, %function
__enable_mmu:
#ifdef CONFIG_ALIGNMENT_TRAP
 orr r0, r0, #CR_A
#else
 bic r0, r0, #CR_A
#endif
#ifdef CONFIG_CPU_DCACHE_DISABLE
 bic r0, r0, #CR_C
#endif
#ifdef CONFIG_CPU_BPREDICT_DISABLE
 bic r0, r0, #CR_Z
#endif
#ifdef CONFIG_CPU_ICACHE_DISABLE
 bic r0, r0, #CR_I
#endif
 mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
        domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
        domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
        domain_val(DOMAIN_IO, DOMAIN_CLIENT))
 mcr p15, 0, r5, c3, c0, 0  @ load domain access register
 mcr p15, 0, r4, c2, c0, 0  @ load page table pointer
 b __turn_mmu_on

/*
 * Enable the MMU.  This completely changes the structure of the visible
 * memory space.  You will not be able to trace execution through this.
 * If you have an enquiry about this, *please* check the linux-arm-kernel
 * mailing list archives BEFORE sending another post to the list.
 *
 *  r0  = cp#15 control register
 *  r13 = *virtual* address to jump to upon completion
 *
 * other registers depend on the function called upon completion
 */
 .align 5
 .type __turn_mmu_on, %function
__turn_mmu_on:
 mov r0, r0
 mcr p15, 0, r0, c1, c0, 0  @ write control reg
 mrc p15, 0, r3, c0, c0, 0  @ read id reg
 mov r3, r3
 mov r3, r3
 mov pc, r13

 

/*
 * 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, r5, r6, r7 corrupted
 *  r4 = physical page table address
 */
 .type __create_page_tables, %function
__create_page_tables: // 235 라인 여기로 점프
 ldr r5, [r8, #MACHINFO_PHYSRAM] @ physram
 pgtbl r4, r5    @ page table address // 매크로 이다. 이런 명령어는 없죠..

adr 은 레이블이 값을 읽는 명령어

 

.macro pgtbl  rd phys

adr   Wrd, siext   (0xa000 / 8000)

sub  Wrd,  Wrd, #0x4000

.end                                                               <-----이렇게 생긴 매크로 이다.

 

 /*
  * 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_MMUFLAGS] @ mmuflags

 

/arch/arm/mm/proc-xscale.S 에 보게 되면 r7에 값이 어떻게 들어가는지 알 수 있다. (0xC0E)

__pxa270_proc_info:
.long 0x69054110
.long 0xfffffff0
.long   PMD_TYPE_SECT | \
PMD_SECT_BUFFERABLE | \
PMD_SECT_CACHEABLE | \
PMD_SECT_AP_WRITE | \
PMD_SECT_AP_READ
b __xscale_setup

 /*
  * 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, lsr #20   @ start of kernel section
 orr r3, r7, r6, lsl #20  @ flags + kernel base 
 str r3, [r4, r6, lsl #2]  @ identity mapping

 /* // r3 에는 결국 0xA000 / 0C0E 가 들어가게 된다.

  * Now setup the pagetables for our kernel direct
  * mapped region.  We round TEXTADDR down to the
  * nearest megabyte boundary.  It is assumed that
  * the kernel fits within 4 contigous 1MB sections.
  */

//TEXTADDR = A0008000 이 들어감
 add r0, r4,  #(TEXTADDR & 0xff000000) >> 18 @ start of kernel
 str r3, [r0, #(TEXTADDR & 0x00f00000) >> 18]! //r0 에는 0xA000 / 7000이 들어감 

//  /arch/arm/kernel/vmlinux.lds.S 에 TEXTADDR 이 정의 되어있다.
 add r3, r3, #1 << 20   // r3 에는 0xA010 / 0C0E 가 들어가는데

// 섹션단위로 다룬다. 1 mega단위로 다루는것이 섹션 그래서 1mega를 추가해 주는것이지요....

 후에 페이지 단위로 바꾸겠지만 서도~~

압축 풀린 커널을 4mega로 보기 때문에 0xA000 / 7000 부터 0xA000 / 7004 ....4개의 주소에 1mega에

해당하는 메모리 할당;; 잘 이해가 안감 ㄷㄷ;;
 str r3, [r0, #4]!   @ KERNEL + 1MB
 add r3, r3, #1 << 20
 str r3, [r0, #4]!   @ KERNEL + 2MB
 add r3, r3, #1 << 20
 str r3, [r0, #4]   @ KERNEL + 3MB

 /*
  * Then map first 1MB of ram in case it contains our boot params.
  */
 add r0, r4, #VIRT_OFFSET >> 18
 orr r6, r5, r7 //r6에는 0xA000 / 0C0E 요 값이 들어감
tr r6, [r0]

#ifdef CONFIG_XIP_KERNEL//  ↓얘네들은 실행 안되는 것들임↓
 /*
  * Map some ram to cover our .data and .bss areas.
  * Mapping 3MB should be plenty.
  */
 sub r3, r4, r5
 mov r3, r3, lsr #20 

 add r0, r0, r3, lsl #2
 add r6, r6, r3, lsl #20
 str r6, [r0], #4
 add r6, r6, #(1 << 20)
 str r6, [r0], #4
 add r6, r6, #(1 << 20)
 str r6, [r0]
#endif

 bic r7, r7, #0x0c   @ turn off cacheable
      @ and bufferable bits
#ifdef CONFIG_DEBUG_LL
 /*
  * 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, we need to map in
  * the 16550-type serial port for the debug messages
  */
 teq r1, #MACH_TYPE_NETWINDER
 teqne r1, #MACH_TYPE_CATS
 bne 1f
 add r0, r4, #0x3fc0   @ ff000000
 mov r3, #0x7c000000
 orr r3, r3, r7
 str r3, [r0], #4
 add r3, r3, #1 << 20
 str r3, [r0], #4
1:
#endif
#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, #0x80   @ 02000000
 mov r3, #0x02000000
 orr r3, r3, r7
 str r3, [r0]
 add r0, r4, #0x3600   @ d8000000
 str r3, [r0]
#endif
 mov pc, lr
 .ltorg

 

/*
 * Exception handling.  Something went wrong and we can't proceed.  We
 * ought to tell the user, but since we don't have any guarantee that
 * we're even running on the right architecture, we do virtually nothing.
 *
 * r0 = ascii error character:
 * a = invalid architecture
 * p = invalid processor
 * i = invalid calling convention
 *
 * Generally, only serious errors cause this.
 */
 .type __error, %function
__error:
#ifdef CONFIG_DEBUG_LL
 mov r8, r0    @ preserve r0
 adr r0, err_str
 bl printascii
 mov r0, r8
 bl printch
#endif
#ifdef CONFIG_ARCH_RPC
/*
 * Turn the screen red on a error - RiscPC only.
 */
 mov r0, #0x02000000
 mov r3, #0x11
 orr r3, r3, r3, lsl #8
 orr r3, r3, r3, lsl #16
 str r3, [r0], #4
 str r3, [r0], #4
 str r3, [r0], #4
 str r3, [r0], #4
#endif
1: mov r0, r0
 b 1b

#ifdef CONFIG_DEBUG_LL
 .type err_str, %object
err_str:
 .asciz "\nError: "
 .align
#endif

/*
 * Read processor ID register (CP#15, CR0), and look up in the linker-built
 * supported processor list.  Note that we can't use the absolute addresses
 * for the __proc_info lists since we aren't running with the MMU on
 * (and therefore, we are not in the correct address space).  We have to
 * calculate the offset.
 *
 * Returns:
 * r3, r4, r6 corrupted
 * r5 = proc_info pointer in physical address space
 * r9 = cpuid
 */    //끝@@
 .type __lookup_processor_type, %function
__lookup_processor_type: // 여기가 두번째로 보는곳 414 라인 실행 되고있는 머신의 프로세서 타입을 찾는다.

 ldmda r3, {r5, r6, r9}
 sub r3, r3, r9   @ get offset between virt&phys
 add r5, r5, r3   @ convert virt addresses to
 add r6, r6, r3   @ physical address space
 mrc p15, 0, r9, c0, c0  @ get processor id // 코프로세서 15번에서 프로세서 id를 읽어옴1: ldmia r5, {r3, r4}   @ value, mask
 and r4, r4, r9   @ mask wanted bits
 teq r3, r4  //exclusive OR   r3 = 0x69054110  r4 = 0xffffffff 가 나올때 까지 계속 br
 beq 2f
 add r5, r5, #PROC_INFO_SZ  @ sizeof(proc_info_list)
 cmp r5, r6
 blt 1b
 mov r5, #0    @ unknown processor
2: mov pc, lr

/*
 * This provides a C-API version of the above function.
 */
ENTRY(lookup_processor_type) stmfd sp!, {r4 - r6, r9, lr}
 bl __lookup_processor_type
 mov r0, r5
 ldmfd sp!, {r4 - r6, r9, pc}

/*
 * Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for

 * more information about the __proc_info and __arch_info structures.
 */
 .long __proc_info_begin
 .long __proc_info_end
3: .long .
 .long __arch_info_begin
 .long __arch_info_end

/*
 * Lookup machine architecture in the linker-build list of architectures.
 * Note that we can't use the absolute addresses for the __arch_info
 * lists since we aren't running with the MMU on (and therefore, we are
 * not in the correct address space).  We have to calculate the offset.
 *
 *  r1 = machine architecture number
 * Returns:
 *  r3, r4, r6 corrupted
 *  r5 = mach_info pointer in physical address space
 */
 .type __lookup_machine_type, %function
__lookup_machine_type: //462라인 머신 타입을 로드한다.
 adr r3, 3b
 ldmia r3, {r4, r5, r6}
 sub r3, r3, r4   @ get offset between virt&phys
 add r5, r5, r3   @ convert virt addresses to
 add r6, r6, r3   @ physical address space
1: ldr r3, [r5]   @ get machine type
 teq r3, r1    @ matches loader number?

//  r1에는 /arch/arm/tools/mach-type 이란 파일에 정의 되어있으며 head-xscale.S에서 r7번 레지스터로 넘겨준 값 아키텍쳐 아이디

 


 beq 2f    @ found
 add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc
 cmp r5, r6
 blt 1b
 mov r5, #0    @ unknown machine
2: mov pc, lr

/*
 * This provides a C-API version of the above function.
 */
ENTRY(lookup_machine_type)
 stmfd sp!, {r4 - r6, lr}
 mov r1, r0
 bl __lookup_machine_type
 mov r0, r5
 ldmfd sp!, {r4 - r6, pc}




'Linux > Boot' 카테고리의 다른 글

세번째head.S /arch/arm/kernel/head.S 분석 .  (0) 2009.06.05
여담...  (0) 2009.06.05
/kernel/head.S  (0) 2009.06.05
head.S  (0) 2009.06.05
head.S  (0) 2009.06.05

WRITTEN BY
RootFriend
개인적으로... 나쁜 기억력에 도움되라고 만들게되었습니다.

,