linux core image,Linux内核源码分析--内核启动之(2)Image内核启动(汇编部分)(Linux-3.0 ARMv7)...

/*

* 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.

*

* 所有32-bit CPU的内核启动代码

*/

#include

#include

#include

#include

#include

#include

#include

#include

#include

#ifdef CONFIG_DEBUG_LL

#include

#endif

/*

*swapper_pg_dir 是初始页表的虚拟地址.

* 我们将页表放在KERNEL_RAM_VADDR以下16K的空间中. 因此我们必须保证

* KERNEL_RAM_VADDR已经被正常设置. 当前, 我们期望的是

* 这个地址的最后16 bits为0x8000, 但我们或许可以放宽这项限制到

* KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000.

*/

#define KERNEL_RAM_VADDR    (PAGE_OFFSET + TEXT_OFFSET)

#if (KERNEL_RAM_VADDR & 0xffff) != 0x8000

#error KERNEL_RAM_VADDR must start at 0xXXXX8000

#endif

.globl    swapper_pg_dir

.equ    swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000

/*

*TEXT_OFFSET 是内核代码(解压后)相对于RAM起始的偏移.

* 而#TEXT_OFFSET - 0x4000就是页表相对于RAM起始的偏移.

* 这个宏的作用是将phys(RAM的启示地址)加上页表的偏移,

* 而得到页表的起始物理地址

*/

.macro    pgtbl, rd, phys

add    \rd, \phys, #TEXT_OFFSET - 0x4000

.endm

#ifdef CONFIG_XIP_KERNEL

#define KERNEL_START    XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)

#define KERNEL_END    _edata_loc

#else

#define KERNEL_START    KERNEL_RAM_VADDR

#define KERNEL_END    _end

#endif

/*

*内核启动入口点.

* ---------------------------

*

* 这个入口正常情况下是在解压完成后被调用的.

* 调用条件: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,

* r1 = machine nr, r2 = atags or dtb pointer.

* 这些条件在解压完成后会被逐一满足,然后才跳转过来。

*

* 这些代码大多数是位置无关的, 如果你的内核入口地址在连接时确定为

* 0xc0008000, 你调用此函数的物理地址就是 __pa(0xc0008000).

*

* 完整的machineID列表,请参见 linux/arch/arm/tools/mach-types

*

* 我们尽量让代码简洁; 不在此处添加任何设备特定的代码

* - 这些特定的初始化代码是boot loader的工作(或在极端情况下,

* 有充分理由的情况下, 可以由zImage完成)。

*/

__HEAD

ENTRY(stext)

setmode    PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ CPU模式设置宏

@ (进入svc模式并且关闭中断)

mrc    p15, 0, r9, c0, c0                       @ 获取处理器id-->r9

bl    __lookup_processor_type                   @ 返回r5=procinfo r9=cpuid

movs    r10, r5                                 @ r10=r5,并可以检测r5=0?注意当前r10的值

THUMB( it    eq )            @ force fixup-able long branch encoding

beq    __error_p            @ yes, error 'p'如果r5=0,则内核处理器不匹配,出错~死循环

/*

*获取RAM的起始物理地址,并保存于 r8 = phys_offset

* XIP内核与普通在RAM中运行的内核不同

* (1)CONFIG_XIP_KERNEL

*         通过运行时计算????

* (2)正常RAM中运行的内核

*         通过编译时确定(PLAT_PHYS_OFFSET 一般在arch/arm/mach-xxx/include/mach/memory.h定义)

*

*/

#ifndef CONFIG_XIP_KERNEL

adr    r3, 2f

ldmia    r3, {r4, r8}

sub    r4, r3, r4            @ (PHYS_OFFSET - PAGE_OFFSET)

add    r8, r8, r4            @ PHYS_OFFSET

#else

ldr    r8, =PLAT_PHYS_OFFSET

#endif

/*

* r1 = machine no, r2 = atags or dtb,

* r8 = phys_offset, r9 = cpuid, r10 = procinfo

*/

bl    __vet_atags            @ 判断r2(内核启动参数)指针的有效性

#ifdef CONFIG_SMP_ON_UP

bl    __fixup_smp            @ ???如果运行SMP内核在单处理器系统中启动,做适当调整

#endif

#ifdef CONFIG_ARM_PATCH_PHYS_VIRT

bl    __fixup_pv_table    @ ????根据内核在内存中的位置修正物理地址与虚拟地址的转换机制

#endif

bl    __create_page_tables    @ 初始化页表!

/*

* 以下使用位置无关的方法调用的是CPU特定代码。

* 详情请见arch/arm/mm/proc-*.S

* r10 = xxx_proc_info 结构体的基地址(在上面__lookup_processor_type函数中选中的)

* 返回时, CPU 已经为 MMU 的启动做好了准备,

* 且 r0 保存着CPU控制寄存器的值.

*/

ldr    r13, =__mmap_switched                @ 在MMU启动之后跳入的第一个虚拟地址

adr    lr, BSYM(1f)                        @ 设置返回的地址(PIC)

mov    r8, r4                                @ 将swapper_pg_dir的物理地址放入r8,

@ 以备__enable_mmu中将其放入TTBR1

ARM(    add    pc, r10, #PROCINFO_INITFUNC    )    @ 跳入构架相关的初始化处理器函数(例如A8的是__v7_setup)

THUMB(    add    r12, r10, #PROCINFO_INITFUNC    )    @主要目的只配置CP15(包括缓存配置)

THUMB(    mov    pc, r12                )

1:    b    __enable_mmu                        @ 启动MMU

ENDPROC(stext)

.ltorg

#ifndef CONFIG_XIP_KERNEL

2:    .long    .

.long    PAGE_OFFSET

#endif

/*

* 创建初始化页表. 我们只创建最基本的页表,

* 以满足内核运行的需要,

* 这通常意味着仅映射内核代码本身.

*

* r8 = phys_offset, r9 = cpuid, r10 = procinfo

*

* 返回:

* r0, r3, r5-r7 被篡改

* r4 = 页表物理地址

*/

__create_page_tables:

pgtbl    r4, r8                @ 现在r4 = 页表的起始物理地址

/*

* 清零16K的一级初始页表区

* 这些页表在内核自解压时被设置过

* (此时MMU已关闭)

*/

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

/*

* 获取节描述符的默认配置(除节基址外的其他配置)

* 这个数据依构架而不同,数据是用汇编文件配置的:

* arch/arm/mm/proc-xxx.S

* (此时MMU已关闭)

*/

ldr    r7, [r10, #PROCINFO_MM_MMUFLAGS] @ 获取mm_mmuflags(节描述符默认配置),保存于r7

/*

* 创建特定映射,以满足__enable_mmu的需求。

* 此特定映射将被paging_init()删除。

*

* 其实这个特定的映射就是仅映射__enable_mmu功能函数区的页表

* 以保证在启用mmu时代码的正确执行--1:1映射(物理地址=虚拟地址)

*/

adr    r0, __enable_mmu_loc

ldmia    r0, {r3, r5, r6}

sub    r0, r0, r3            @ 获取编译时确定的虚拟地址到当前物理地址的偏移

add    r5, r5, r0            @ __enable_mmu的当前物理地址

add    r6, r6, r0            @ __enable_mmu_end的当前物理地址

mov    r5, r5, lsr #20        @ __enable_mmu的节基址

mov    r6, r6, lsr #20        @ __enable_mmu_end的节基址

1:    orr    r3, r7, r5, lsl #20        @ 生成节描述符:flags + 节基址

str    r3, [r4, r5, lsl #2]    @ 设置节描述符,1:1映射(物理地址=虚拟地址)

teq    r5, r6                    @ 完成映射?(理论上一次就够了,这个函数应该不会大于1M吧~)

addne    r5, r5, #1            @ r5 = 下一节的基址

bne    1b

/*

* 现在创建内核的逻辑映射区页表(节映射)

* 创建范围:KERNEL_START---KERNEL_END

* KERNEL_START:内核最终运行的虚拟地址

* KERNEL_END:内核代码结束的虚拟地址(bss段之后,但XIP不是)

*/

mov    r3, pc                @ 获取当前物理地址

mov    r3, r3, lsr #20        @ r3 = 当前物理地址的节基址

orr    r3, r7, r3, lsl #20    @ r3 为当前物理地址的节描述符

/*

* 下面是为了确定页表项的入口地址

* 其实页表入口项的偏移就反应了对应的虚拟地址的高位

*

* 由于ARM指令集的8bit位图问题,只能分两次得到

* KERNEL_START:内核最终运行的虚拟地址

*

*/

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    @ r6 = 内核逻辑映射结束的节基址

1:    cmp    r0, r6

add    r3, r3, #1 << 20    @ 生成节描述符(只需做基址递增)

strls    r3, [r0], #4    @ 设置节描述符

bls    1b

#ifdef CONFIG_XIP_KERNEL

/*

* 如果是XIP技术的内核,上面的映射只能映射内核代码和只读数据部分

* 这里我们再映射一些RAM来作为 .data and .bss 空间.

*/

add    r3, r8, #TEXT_OFFSET

orr    r3, r3, r7            @ 生成节描述符:flags + 节基址

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

/*

* 然后映射启动参数区(现在r2中的atags物理地址)

* 或者

* 如果启动参数区的虚拟地址没有确定(或者无效),则会映射RAM的头1MB.

*/

mov    r0, r2, lsr #20

movs    r0, r0, lsl #20

moveq    r0, r8                @ 如果atags指针无效,则r0 = r8(映射RAM的头1MB)

sub    r3, r0, r8

add    r3, r3, #PAGE_OFFSET    @ 转换为虚拟地址

add    r3, r4, r3, lsr #18        @ 确定页表项(节描述符)入口地址

orr    r6, r7, r0                @ 生成节描述符

str    r6, [r3]                @ 设置节描述符

/*

* 下面是调试信息的输出函数区

* 这里做了IO内存空间的节映射

*/

#ifdef CONFIG_DEBUG_LL

#ifndef CONFIG_DEBUG_ICEDCC

/*

* 为串口调试映射IO内存空间(将串口IO内存之上的所有地址都映射了)

* 这允许调试信息(在paging_init之前)从串口控制台输出

*

*/

addruart r7, r3        @ 宏代码,位于arch/arm/mach-xxx/include/mach/debug-macro.S

@ 作用是将串口控制寄存器的基址放入r7(物理地址)和r3(虚拟地址)

mov    r3, r3, lsr #20

mov    r3, r3, lsl #2

add    r0, r4, r3        @ r0为串口IO内存映射页表项的入口地址

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

cmp    r3, #0x0800            @ limit to 512MB,入口地址有效性检查(只能在最后#0x0800内)

movhi    r3, #0x0800        @ 也就是说虚拟地址被限制在3.5G以上

add    r6, r0, r3            @ r6为页表结束地址

mov    r3, r7, lsr #20

ldr    r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags

orr    r3, r7, r3, lsl #20    @ 生成节描述符

1:    str    r3, [r0], #4

add    r3, r3, #1 << 20

teq    r0, r6

bne    1b

#else /* CONFIG_DEBUG_ICEDCC */

/* 我们无需任何串口调试映射 for ICEDCC */

ldr    r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags

#endif /* !CONFIG_DEBUG_ICEDCC */

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

/*

* 如果我们在使用 NetWinder 或 CATS,我们也需要为调试信息映射

* 16550-type 串口

*/

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)

.ltorg

.align

__enable_mmu_loc:

.long    .

.long    __enable_mmu

.long    __enable_mmu_end

#if defined(CONFIG_SMP)

__CPUINIT

ENTRY(secondary_startup)

/*

* Common entry point for secondary CPUs.

*

* Ensure that we're in SVC mode, and IRQs are disabled. Lookup

* the processor type - there is no need to check the machine type

* as it has already been validated by the primary processor.

*/

setmode    PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9

mrc    p15, 0, r9, c0, c0        @ get processor id

bl    __lookup_processor_type

movs    r10, r5                @ invalid processor?

moveq    r0, #'p'            @ yes, error 'p'

THUMB( it    eq )        @ force fixup-able long branch encoding

beq    __error_p

/*

* Use the page tables supplied from __cpu_up.

*/

adr    r4, __secondary_data

ldmia    r4, {r5, r7, r12}        @ address to jump to after

sub    lr, r4, r5            @ mmu has been enabled

ldr    r4, [r7, lr]            @ get secondary_data.pgdir

add    r7, r7, #4

ldr    r8, [r7, lr]            @ get secondary_data.swapper_pg_dir

adr    lr, BSYM(__enable_mmu)        @ return address

mov    r13, r12            @ __secondary_switched address

ARM(    add    pc, r10, #PROCINFO_INITFUNC    ) @ initialise processor

@ (return control reg)

THUMB(    add    r12, r10, #PROCINFO_INITFUNC    )

THUMB(    mov    pc, r12                )

ENDPROC(secondary_startup)

/*

* r6 = &secondary_data

*/

ENTRY(__secondary_switched)

ldr    sp, [r7, #4]            @ get secondary_data.stack

mov    fp, #0

b    secondary_start_kernel

ENDPROC(__secondary_switched)

.align

.type    __secondary_data, %object

__secondary_data:

.long    .

.long    secondary_data

.long    __secondary_switched

#endif /* defined(CONFIG_SMP) */

/*

* 在最后启动MMU前,设置一些常用位 Essentially

* 其实,这里只是加载了页表指针和域访问控制数据寄存器

*

*

* r0 = cp#15 control register

* r1 = machine ID

* r2 = atags or dtb pointer

* r4 = page table pointer

* r9 = processor ID

* r13 = 最后要跳入的虚拟地址

*/

__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        @ 载入域访问控制数据到DACR

mcr    p15, 0, r4, c2, c0, 0        @ 载入页表基址到TTBR0

b    __turn_mmu_on                @ 开启MMU

ENDPROC(__enable_mmu)

/*

* 使能 MMU. 这完全改变了可见的内存地址空间结构。

* 您将无法通过这里跟踪执行。

* 如果你已对此进行探究, *请*在向邮件列表发送另一个新帖之前,

* 检查linux-arm-kernel的邮件列表归档

*

* r0 = cp#15 control register

* r1 = machine ID

* r2 = atags or dtb pointer

* r9 = processor ID

* r13 = 最后要跳入的*虚拟*地址

*

* 其他寄存器依赖上面的调用函数

*/

.align    5

__turn_mmu_on:

mov    r0, r0

mcr    p15, 0, r0, c1, c0, 0        @ 设置cp#15控制寄存器(启用MMU)

mrc    p15, 0, r3, c0, c0, 0        @ read id reg

mov    r3, r3

mov    r3, r13                        @ r3中装入最后要跳入的*虚拟*地址

mov    pc, r3                        @ 跳转到__mmap_switched

__enable_mmu_end:

ENDPROC(__turn_mmu_on)

#ifdef CONFIG_SMP_ON_UP

__INIT

__fixup_smp:

and    r3, r9, #0x000f0000    @ architecture version

teq    r3, #0x000f0000        @ CPU ID supported?

bne    __fixup_smp_on_up    @ no, assume UP

bic    r3, r9, #0x00ff0000

bic    r3, r3, #0x0000000f    @ mask 0xff00fff0

mov    r4, #0x41000000

orr    r4, r4, #0x0000b000

orr    r4, r4, #0x00000020    @ val 0x4100b020

teq    r3, r4            @ ARM 11MPCore?

moveq    pc, lr            @ yes, assume SMP

mrc    p15, 0, r0, c0, c0, 5    @ read MPIDR

and    r0, r0, #0xc0000000    @ multiprocessing extensions and

teq    r0, #0x80000000        @ not part of a uniprocessor system?

moveq    pc, lr            @ yes, assume SMP

__fixup_smp_on_up:

adr    r0, 1f

ldmia    r0, {r3 - r5}

sub    r3, r0, r3

add    r4, r4, r3

add    r5, r5, r3

b    __do_fixup_smp_on_up

ENDPROC(__fixup_smp)

.align

1:    .word    .

.word    __smpalt_begin

.word    __smpalt_end

.pushsection .data

.globl    smp_on_up

smp_on_up:

ALT_SMP(.long    1)

ALT_UP(.long    0)

.popsection

#endif

.text

__do_fixup_smp_on_up:

cmp    r4, r5

movhs    pc, lr

ldmia     {r0, r6}

ARM(    str    r6, [r0, r3]    )

THUMB(    add    r0, r0, r3    )

#ifdef __ARMEB__

THUMB(    mov    r6, r6, ror #16    )    @ Convert word order for big-endian.

#endif

THUMB(    strh    r6, [r0], #2    )    @ For Thumb-2, store as two halfwords

THUMB(    mov    r6, r6, lsr #16    )    @ to be robust against misaligned r3.

THUMB(    strh    r6, [r0]    )

b    __do_fixup_smp_on_up

ENDPROC(__do_fixup_smp_on_up)

ENTRY(fixup_smp)

stmfd     {r4 - r6, lr}

mov    r4, r0

add    r5, r0, r1

mov    r3, #0

bl    __do_fixup_smp_on_up

ldmfd     {r4 - r6, pc}

ENDPROC(fixup_smp)

#ifdef CONFIG_ARM_PATCH_PHYS_VIRT

/* __fixup_pv_table - patch the stub instructions with the delta between

* PHYS_OFFSET and PAGE_OFFSET, which is assumed to be 16MiB aligned and

* can be expressed by an immediate shifter operand. The stub instruction

* has a form of '(add|sub) rd, rn, #imm'.

*/

__HEAD

__fixup_pv_table:

adr    r0, 1f

ldmia    r0, {r3-r5, r7}

sub    r3, r0, r3    @ PHYS_OFFSET - PAGE_OFFSET

add    r4, r4, r3    @ adjust table start address

add    r5, r5, r3    @ adjust table end address

add    r7, r7, r3    @ adjust __pv_phys_offset address

str    r8, [r7]    @ save computed PHYS_OFFSET to __pv_phys_offset

#ifndef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT

mov    r6, r3, lsr #24    @ constant for add/sub instructions

teq    r3, r6, lsl #24 @ must be 16MiB aligned

#else

mov    r6, r3, lsr #16    @ constant for add/sub instructions

teq    r3, r6, lsl #16    @ must be 64kiB aligned

#endif

THUMB(    it    ne        @ cross section branch )

bne    __error

str    r6, [r7, #4]    @ save to __pv_offset

b    __fixup_a_pv_table

ENDPROC(__fixup_pv_table)

.align

1:    .long    .

.long    __pv_table_begin

.long    __pv_table_end

2:    .long    __pv_phys_offset

.text

__fixup_a_pv_table:

#ifdef CONFIG_THUMB2_KERNEL

#ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT

lsls    r0, r6, #24

lsr    r6, #8

beq    1f

clz    r7, r0

lsr    r0, #24

lsl    r0, r7

bic    r0, 0x0080

lsrs    r7, #1

orrcs r0, #0x0080

orr    r0, r0, r7, lsl #12

#endif

1:    lsls    r6, #24

beq    4f

clz    r7, r6

lsr    r6, #24

lsl    r6, r7

bic    r6, #0x0080

lsrs    r7, #1

orrcs    r6, #0x0080

orr    r6, r6, r7, lsl #12

orr    r6, #0x4000

b    4f

2:    @ at this point the C flag is always clear

add r7, r3

#ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT

ldrh    ip, [r7]

tst    ip, 0x0400    @ the i bit tells us LS or MS byte

beq    3f

cmp    r0, #0        @ set C flag, and ...

biceq    ip, 0x0400    @ immediate zero value has a special encoding

streqh    ip, [r7]    @ that requires the i bit cleared

#endif

3:    ldrh    ip, [r7, #2]

and    ip, 0x8f00

orrcc    ip, r6    @ mask in offset bits 31-24

orrcs    ip, r0    @ mask in offset bits 23-16

strh    ip, [r7, #2]

4:    cmp    r4, r5

ldrcc    r7, [r4], #4    @ use branch for delay slot

bcc    2b

bx    lr

#else

#ifdef CONFIG_ARM_PATCH_PHYS_VIRT_16BIT

and    r0, r6, #255    @ offset bits 23-16

mov    r6, r6, lsr #8    @ offset bits 31-24

#else

mov    r0, #0        @ just in case...

#endif

b    3f

2:    ldr    ip, [r7, r3]

bic    ip, ip, #0x000000ff

tst    ip, #0x400    @ rotate shift tells us LS or MS byte

orrne    ip, ip, r6    @ mask in offset bits 31-24

orreq    ip, ip, r0    @ mask in offset bits 23-16

str    ip, [r7, r3]

3:    cmp    r4, r5

ldrcc    r7, [r4], #4    @ use branch for delay slot

bcc    2b

mov    pc, lr

#endif

ENDPROC(__fixup_a_pv_table)

ENTRY(fixup_pv_table)

stmfd     {r4 - r7, lr}

ldr    r2, 2f            @ get address of __pv_phys_offset

mov    r3, #0            @ no offset

mov    r4, r0            @ r0 = table start

add    r5, r0, r1        @ r1 = table size

ldr    r6, [r2, #4]        @ get __pv_offset

bl    __fixup_a_pv_table

ldmfd     {r4 - r7, pc}

ENDPROC(fixup_pv_table)

.align

2:    .long    __pv_phys_offset

.data

.globl    __pv_phys_offset

.type    __pv_phys_offset, %object

__pv_phys_offset:

.long    0

.size    __pv_phys_offset, . - __pv_phys_offset

__pv_offset:

.long    0

#endif

#include "head-common.S"

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值