Thursday, August 16, 2012

ARM Linux Interrupts Code Flow


ARM Vector Table

vector_irq         -> Interrupt Dispatcher
vector_dabt      -> Data Abort Dispatcher
vector_pabt      -> Perfetch Abort Dispatcher
vector_und       -> Undefined Instruction Dispatcher
vector_fiq        -> Fast Interrupt Dispatcher
vector_addrexcptn   -> Address Exception Dispatcher

Once an exception occurs , one of the above functions gets called.

IRQ Exceptions:

From there ,
/* Assembly Code */
vector_irq   ->> Depending on the ARM mode call the corresponding function
irq_svc
irq_handler
arch_irq_handler_default(arch/arm/kernel) ->> get_irqnr_preamble(arch/arm/mach-omap2) -> set Soc Specific irq base
->>  get_irqnr_and_base -> Check Pending Interrupts

/* C Code */
asm_do_IRQ ->> routine called with r0 = irq number, r1 = struct pt_regs *

Data Aborts:
Control Processor Register is CP15 DFSR & DFAR Register.

vector_dabt(called from ROM code)
(
__dabt_svc | __dabt_usr
)
do_DataAbort

(
do_translation_fault |
do_page_fault |
do_sect_fault |
)


Prefetch Aborts:
Control Processor Register is CP15 IFSR & IFAR Register.

vector_pabt(called from ROM code)
(
__pabt_svc | __pabt_usr
)
do_PrefetchAbort

(
do_translation_fault |
do_page_fault |
do_sect_fault |
)

List of Instruction Faults


static struct fsr_info fsr_info[] = {
        { do_bad,               SIGBUS,  0,             "unknown 0"                     },
        { do_bad,               SIGBUS,  0,             "unknown 1"                     },
        { do_bad,               SIGBUS,  0,             "unknown 2"                     },
        { do_bad,               SIGBUS,  0,             "unknown 3"                     },
        { do_bad,               SIGBUS,  0,             "reserved translation fault"    },
        { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 1 translation fault"     },
        { do_translation_fault, SIGSEGV, SEGV_MAPERR,   "level 2 translation fault"     },
        { do_page_fault,        SIGSEGV, SEGV_MAPERR,   "level 3 translation fault"     },
        { do_bad,               SIGBUS,  0,             "reserved access flag fault"    },
        { do_bad,               SIGSEGV, SEGV_ACCERR,   "level 1 access flag fault"     },
        { do_bad,               SIGSEGV, SEGV_ACCERR,   "level 2 access flag fault"     },
        { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 3 access flag fault"     },
        { do_bad,               SIGBUS,  0,             "reserved permission fault"     },
        { do_bad,               SIGSEGV, SEGV_ACCERR,   "level 1 permission fault"      },
        { do_sect_fault,        SIGSEGV, SEGV_ACCERR,   "level 2 permission fault"      },
        { do_page_fault,        SIGSEGV, SEGV_ACCERR,   "level 3 permission fault"      },
        { do_bad,               SIGBUS,  0,             "synchronous external abort"    },
        { do_bad,               SIGBUS,  0,             "asynchronous external abort"   },
        { do_bad,               SIGBUS,  0,             "unknown 18"                    },
        { do_bad,               SIGBUS,  0,             "unknown 19"                    },
        { do_bad,               SIGBUS,  0,             "synchronous abort (translation table walk)" },
        { do_bad,               SIGBUS,  0,             "synchronous abort (translation table walk)" },
        { do_bad,               SIGBUS,  0,             "synchronous abort (translation table walk)" },
        { do_bad,               SIGBUS,  0,             "synchronous abort (translation table walk)" },
        { do_bad,               SIGBUS,  0,             "synchronous parity error"      },
        { do_bad,               SIGBUS,  0,             "asynchronous parity error"     },
        { do_bad,               SIGBUS,  0,             "unknown 26"                    },
        { do_bad,               SIGBUS,  0,             "unknown 27"                    },
        { do_bad,               SIGBUS,  0,             "synchronous parity error (translation table walk" },
        { do_bad,               SIGBUS,  0,             "synchronous parity error (translation table walk" },
        { do_bad,               SIGBUS,  0,             "synchronous parity error (translation table walk" },
        { do_bad,               SIGBUS,  0,             "synchronous parity error (translation table walk" },
        { do_bad,               SIGBUS,  0,             "unknown 32"                    },
        { do_bad,               SIGBUS,  BUS_ADRALN,    "alignment fault"               },
        { do_bad,               SIGBUS,  0,             "debug event"                   },
        { do_bad,               SIGBUS,  0,             "unknown 35"                    },
        { do_bad,               SIGBUS,  0,             "unknown 36"                    },
        { do_bad,               SIGBUS,  0,             "unknown 37"                    },
        { do_bad,               SIGBUS,  0,             "unknown 38"                    },
        { do_bad,               SIGBUS,  0,             "unknown 39"                    },
        { do_bad,               SIGBUS,  0,             "unknown 40"                    },
        { do_bad,               SIGBUS,  0,             "unknown 41"                    },
        { do_bad,               SIGBUS,  0,             "unknown 42"                    },
        { do_bad,               SIGBUS,  0,             "unknown 43"                    },
        { do_bad,               SIGBUS,  0,             "unknown 44"                    },
        { do_bad,               SIGBUS,  0,             "unknown 45"                    },
        { do_bad,               SIGBUS,  0,             "unknown 46"                    },
        { do_bad,               SIGBUS,  0,             "unknown 47"                    },
        { do_bad,               SIGBUS,  0,             "unknown 48"                    },
        { do_bad,               SIGBUS,  0,             "unknown 49"                    },
        { do_bad,               SIGBUS,  0,             "unknown 50"                    },
        { do_bad,               SIGBUS,  0,             "unknown 51"                    },
        { do_bad,               SIGBUS,  0,             "implementation fault (lockdown abort)" },
        { do_bad,               SIGBUS,  0,             "unknown 53"                    },
        { do_bad,               SIGBUS,  0,             "unknown 54"                    },
        { do_bad,               SIGBUS,  0,             "unknown 55"                    },
        { do_bad,               SIGBUS,  0,             "unknown 56"                    },
        { do_bad,               SIGBUS,  0,             "unknown 57"                    },
        { do_bad,               SIGBUS,  0,             "implementation fault (coprocessor abort)" },
        { do_bad,               SIGBUS,  0,             "unknown 59"                    },
        { do_bad,               SIGBUS,  0,             "unknown 60"                    },
        { do_bad,               SIGBUS,  0,             "unknown 61"                    },
        { do_bad,               SIGBUS,  0,             "unknown 62"                    },
        { do_bad,               SIGBUS,  0,             "unknown 63"                    },
};



Interprocessor Interrupts

IPIs are used to maintain synchronisation between the processors. For example, if a kernel page table entry changes, both processors must either flush their TLBs or invalidate that particular page table entry. Whichever processor changed the mapping knows to do this automatically, but the other processor does not; therefore, the processor which changed the mapping must send an IPI to the other processor to tell it to flush its TLB or invalidate the page table entry.
Using the local APIC, you can send interrupts to all processors, all processors but the one sending the interrupt, or a specific processor or logical address as well as self-interrupts. To send an IPI, write the destination APIC ID, if needed, into the high word of the ICR, then write the low word of ICR with the destination shorthand and interrupt vector set to send the IPI. Be sure to wrap these functions in spinlocks. You might want to turn off interrupts as well while sending IPIs.

31  Private nIRQ - Each CPU only sees its own legacy nIRQ pin
30  Private watchdog - Each CPU has its own watchdog timer
29  Private timer - Each CPU has its own timer
28:16  Spare - available for future private peripherals
15:0    Private inter-processor interrupts - See below

Inter-processor interrupts (IPI) can, for example, be used for interrupt-driven communication
between CPUs in an SMP system. The GIC implements IPI using 16 software interrupts. To
generate an IPI, an MPCore CPU must write to the primary GIC's Software Interrupt register
specifying an interrupt ID in the range 0 to 15 and which CPU(s) will receive the interrupt.




Technique
Recommended for
Not recommended for
a) Uncached mappings
Infrequently or sparsely accessed DMA data blocks
Simplest implementation
Correctness/reliability
Intensively accessed DMA blocks will not benefit from caching, in which case performance can suffer.
b) Set affinity
Minimizing cache line migration overheads

In complex applications, a manual procedure will be required to map the IRQ part of device drivers and user processes to CPUs.  Thorough testing will be required.
In complex applications, the enforced mapping of processes to CPUs might mean that the maximum aggregate performance is not achievable due to unbalanced CPU loading.
c) Read for ownership
Small DMA data blocks.
Can be done by OS kernel
Large DMA data blocks – must read/write completely – will cause extra memory traffic and potentially thrash L1 cache.
d) Broadcast of cache maintenance operations
Can be done by OS kernel
Currently done by Linux 2.6.28-arm1
Small DMA blocks – overhead of synchronous IPI is likely to be significant.
Performance – scheduling of IPI depends on IRQ loading –  process blocks until all CPUs have responded.

Wednesday, August 15, 2012

ARM Exceptions_Fault_Aborts


Translation fault
There are two types of translation fault:
Section
A section translation fault occurs if:
The TLB tries to perform a page table walk but the page table walk is disabled by one of the PD0 or PD1 bits.
The TLB fetches a first level translation table descriptor, and this first level descriptor is invalid.
This is the case when bits[1:0] of this descriptor are b00 or b11.

Page
A page translation fault occurs if the TLB fetches a second-level translation table descriptor and this descriptor is marked as invalid, bits [1:0] = b00.

Arm Linux Bootup + MMU : Assembly Code Walkthrough



PAGE_OFFSET ->> 0xC0000000

reset

x-loader

uboot

Kernel Decompressor ->> Decompresses the Kernel to 0xC0008000

Kernel Starts ->> MMU Off , I-Dont care, D-Cache off , r0 = 0 , r1=machine nr , r2 = atags_pointer
arch/arm/kernel/head.S
Entry Point -> stext

Ensure Supervisor mode
Irqs disabled
lookup_processor_type ->>r5=procinfo , r9=cpuid
/*
* r1 = machine no, r2 = atags or dtb,
* r8 = phys_offset, r9 = cpuid, r10 = procinfo
*/
__vet_atags
__fixup_smp
__create_page_tables
/*
* 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 = phys_offset, r9 = cpuid, r10 = procinfo
*
* Returns:
*  r0, r3, r5-r7 corrupted
*  r4 = physical page table address
*/

        /*
* Clear the swapper page table
*/

        /*
* Create identity mapping to cater for __enable_mmu.
* This identity mapping will be removed by paging_init().
*/

        /*
* Now setup the pagetables for our kernel direct
* mapped region.
*/

        /*
* Then map boot params address in r2 or the first 1MB (2MB with LPAE)
* of ram if boot params address is not specified.
*/

        /*
* Map in IO space for serial debugging.
* This allows debug messages to be output
* via a serial console before paging_init.
*/
set R13 = __mmap_switched  -> Function executed after mmu is enabled.
TTBR1 is set to swapper_pg_dir
enable_mmu
__mmap_switched ->> virtual address
start_kernel

Arm MMU in linux: page table Initialization


This post covers details of MMU initialization code of linux 2.6.11 kernel for ARM and few internal tweaks used for mapping arm page tables to x86 style page tables which linux memory management code expects. We'll cover only architecutre specific details, generic MM code of kernel will not be covered here. For all the architecutre specific details we take ARM 11 (Architecture V6) as our reference. 


Following conventions are used in this writeup:

  • All details are for uniprocessor system, nothing related to SMP is covered here.
Kernel's execution starts at stext in (arch/arm/kernel/head.S) , at this point kernel expects that MMU is off, I-cache is off, D-cache is off. After looking up of processor type and machine number, we call__create_page_tables which sets up the initial MMU tables for which are used only during initialization of MMU. Here we create MMU mapping tables for 4MB of memory starting from kernel's text (More preciesly it starts at a 1MB section in which kernel's text starts). Following is the code snippet which creates these mappings: 

-----------------------------

_create_page_tables: 


  1. ldr r5, [r8, #MACHINFO_PHYSRAM] @ physram
  2. pgtbl r4, r5 @ page table address
  3. mov r0, r4
  4. mov r3, #0
  5. add r6, r0, #0x4000
  6. 1: str r3, [r0], #4
  7. str r3, [r0], #4
  8. str r3, [r0], #4
  9. str r3, [r0], #4
  10. teq r0, r6
  11. bne 1b
  12. ldr r7, [r10, #PROCINFO_MMUFLAGS] @ mmuflags
  13. mov r6, pc, lsr #20 @ start of kernel section
  14. orr r3, r7, r6, lsl #20 @ flags + kernel base
  15. str r3, [r4, r6, lsl #2] @ identity mapping
  16. add r0, r4, #(TEXTADDR & 0xff000000) >> 18 @ start of kernel
  17. str r3, [r0, #(TEXTADDR & 0x00f00000) >> 18]!
  18. add r3, r3, #1 <<>
  19. str r3, [r0, #4]! @ KERNEL + 1MB
  20. add r3, r3, #1 <<>
  21. str r3, [r0, #4]! @ KERNEL + 2MB
  22. add r3, r3, #1 <<>
  23. str r3, [r0, #4] @ KERNEL + 3MB


-----------------------------
In this function we first of all find out the physical address where our RAM starts, and locate the address where we'll store our inital MMU tables (line #1 and #2). We create these inital MMU tables 16kb below kernel entry point. Line #12- #15 create an identity mapping of 1MB starting from kernel entry point (physical address of kernel entry, i.estext). Here we do'nt create a second level page table for these mapping, instead of that we specify in first level descriptor that these mappings are for section (each section mapping is of size 1MB). Simiarly we create mapping for 4 more sections (these 4 are non identity mappings, size of each section is 1MB here also) starting at TEXTADDR (virtual address of kernel entry point)


so the initial memory map looks something line this:
After the initial page tables are setup, next step is to enable MMU. This code is tricky, does lot of deep magic. 
----------------------------


  1. ldr r13, __switch_data @ address to jump to after mmu has been enabled
  2. adr lr, __enable_mmu @ return (PIC) address
  3. add pc, r10, #PROCINFO_INITFUNC

  4. .type __switch_data, %object

  5. __switch_data: .long __mmap_switched
  6. .long __data_loc @ r4
  7. .long __data_start @ r5
  8. .long __bss_start @ r6
  9. .long _end @ r7
  10. .long processor_id @ r4
  11. .long __machine_arch_type @ r5
  12. .long cr_alignment @ r6
  13. .long init_thread_union+8192 @ sp
-----------------------------


line #1 puts virtual address of __mmap_switched in r13, after enabling MMU kernel will jump to address in r13. The virtual address that is used here is not from identity mapping, but it is PAGE_OFFSET + physical address of __mmap_switched. And now since in__mmap_switched we start refering to virtual addresses of variables and functions, so starting from __mmap_switched is no longer position independent.
At line #2-#3, we put position independent address of__enable_mmu ('adr' Psuedo instruction translates to PC relative addressing, that's why it is position independent) and jumps to at a offset of PROCINFO_INITFUNC (12 bytes) in __v6_proc_info structure (arch/arm/mm/proc-v6.S). At PROCINFO_INITFUNC in__v6_proc_info we have a branch to __v6_setup, which does following setup for enabling MMU:

  • Clean and Invalidate D-cache and Icache, and invalidate TLB.
  • Prepare the control register1 (C1) value that needs to be written when enabling mmu, and return the value which needs to be written in C1.
As __v6_setup returns we enter __enable_mmu, which just sets Cache enable bits, branch-prediction enable bits in 'r' which is the value to be written in C1 (value to be written in C1 was returned in 'r0' by __v6_setup) and then calls __turn_mmu_on.
--------------------------------------
__turn_mmu_on:


  1. mov r0, r0
  2. mcr p15, 0, r0, c1, c0, 0 @ write control reg
  3. mrc p15, 0, r3, c0, c0, 0 @ read id reg
  4. mov r3, r3
  5. mov r3, r3
  6. mov pc, r13
--------------------------------------

__turn_mmu_on 
just writes 'r0' to C1 to enable MMU. Line #4 and #5 are the nops to make sure that pipeline does not contain and invalid address access when C1 is written. Line #6 'r13' is moved in 'pc' and we enter __mmap_switched. Now MMU is ON, every address is virtual no physical addresses anymore. But the final kernel page tables are still not setup (final page tables will be setup bypaging_init and mappings created by __create_page_tables will be discarded), we are still running with 4Mb mapping that__create_page_tables had set it for us. __mmap_switched copies the data segment if required, clears the BSS and calls start_kernel.

The mappings page table mappings that we discussed above makes sure that the position dependent code of kernel startup runs peacefully, and these mappings are overwritten at later stages by a function called paging_init( ) start_kernel( ) -> setup_arch( )-> paging_init( )).
paging_init() populates the master L1 page table (init_mm) with linear mappings of complete SDRAM and the SOC specific address space (SOC specific memory mappings are created by mdesc->mapio()function, this function pointer is initialized by SOS specific code, arch/arm/mach-*). So in master L1 page table (init_mm) we have mappings which map virtual addresses in range PAGE_OFFSET - (PAGE_OFFSET + sdram size) to physical address of SDRAM start - (physical address of SDRAM start + sdram size). Also we have SOC specific mappings created by mdesc->mapio() function.
One more point worth noting here is that whenever a new process is created, a new L1 page table is allocated for it, and the kernel mappings (sdram mapping, SOC specific mappings) are copied to it from the master L1 page table (init_mm). Every process has its own user space mappings so no need to copy anything from anywhere.
Handling of mapings for VMALLOC REGION are is bit tricky, because VMALLOC virtual addressed are allocated when a process callsvmalloc( ). So if we have some processes' which were created before the process which called vmalloc then their L1 page tables will have no maping for new vmalloc'ed region. So how this is taken care of is very simple, the mappings for vmalloc'ed region are updated in the master L1 page table (init_mm) and when a process whose page tables do not have newly created mapping accesses the newly vmalloc'ed region, a page fault is generated. And kernel handles page faults in VMALLOC region specially by copying the mappings for newly vmalloc'ed area to page tables of the process which generated the fault.
Tweaks for integerating ARM 2 level page tables with linux implementation of 3 level ix86 style page tables
1. Linux assumes that it is dealing with 3 level page tables and ARM has 2 level page tables. For handling this, for ARM in ARCH include files __pmd is defined as a identity macro in (include/asm-arm/page.h):
-------------------------------------------------
#define __pmd(x) ((pmd_t) { (x) } )
--------------------------------------------------
So effectively for ARM, linux is told that pmd has just one entry, effectively bypassing pmd.
2. Memory management code of in Linux expects ix86 type page table entries, for example it uses 'P' (present ), 'D' (dirty) bits but ARM page table entries (PTEs) don't have these bits. As a workaround to provide ix86 PTE flags, what ARM page table implementation does is that, it tells linux that PGD has 2048 entries of 8 bytes each (whereas ARM hardware level 1 pagetable has 4096 entries of 4 bytes each).

Also it tells Linux that each PTE table has 512 entries (whereas ARM hardware PTE table of 256 entries).This means that the PTE table that is exposed to Linux is actually 2 ARM PTE tables, arranged contigously in memory. NowAfter these 2 ARM PTE tables (lets say h/w PTE table 1 and h/w PTE table 2), 2 more PTE tables (256 entries each, say linux PTE table 1 and 2) are allocated in memory contiguous to Arm hardware page table 2. Linux PTE table 1 and 2 contains PTE flags of ix86 style corresponding to entries in ARM PTE table 1 and 2. So whenever Linux needs ix86 style PTE flags it uses entries in Linux PTE table 1 and 2. ARM never looks into Linux PTE table 1 and 2 during hardware page table walk, it uses only h/w PTE tables as mentioned above. Refer to include/asm-arm/pgtable.h(line: 20-76) for details of how ARM h/w PTE table 1 and 2, and linux PTE table 1 and 2 are organized in memory.

So here we conclude the architecture specific page table related stuff for ARM.


Tuesday, August 14, 2012

Code Flow of X-Loader



Code Flow of X-Loader

reset                   -> start.s
-> boot mode,
relocate uboot to ram(after cpu_crit_init completes)
stack setup

cpu_crit_init
-> Invalidate ITLBs , Icache , BP Array
Disable MMU & Caches (MMU is disable during reset)

lowlevel_init        -> platform.s
s_init           -> cpu/omap4/cpu.c
mux configuration -> Mux configuration for all the pins
voltage core setup -> set voltages of the voltage domains
sdram init -> configuring the clock , power .. of the memory controller
prcm init -> configure the Main System clock & dpll

 _start_armboot   ->lib/board.c
init_sequence
cpu_init -> omap revision
set L2 Cache Controller prefetch
Enable data prefetch
Voltage controls
board_init
serial_init
enable_ap_uart -> gpio settings for uart
print_info
nandinfo
Read U-boot from the selected device (size of u-boot -> 0x140000).
start uboot @ 0x80008000...

How to edit Ramdisk

Steps for editing the ramdisk.

mv ramdisk.img ramdisk.cpio.gz
gzip -d ramdisk.cpio.gz
mkdir root_fs
cp ramdisk.cpio root_fs/
cd root_fs
cpio -i -F ramdisk.cpio
rm ramdisk.cpio

/* Make the Changes*/

cpio -i -t -F ../ramdisk.cpio | cpio -o -H newc -O ../ramdisk_new.cpio
find . | cpio -o -H newc | gzip > ../newramdisk.cpio.gz
cd ..
mv newramdisk.cpio.gz ramdisk.img
rm -rf root_fs
rm ramdisk.cpio ramdisk_new.cpio

vmlinux linked output


The individual sections are created by compiler and assembler.
The linker using the linker script creates the vmlinux with the following structure.
See the attached System.map file for the vmlinux structure.

 * OUTPUT_FORMAT(...)
 * OUTPUT_ARCH(...)
 * ENTRY(...)
 * SECTIONS
 * {
 *      . = START;
 *      __init_begin = .;
 *      HEAD_TEXT_SECTION ->> .head.text
 *      INIT_TEXT_SECTION(PAGE_SIZE) ->> _sinittext
.init.text .devinit.text .cpuinit.text .meminit.text
_sinittext

 *      INIT_DATA_SECTION(...) ->> .init.data .devinit.data .cpuinit.data .meminit.data
__ctors_start .ctors __ctors_end
.init.rodata
__start_mcount_loc __mcount_loc __stop_mcount_loc
__start_ftrace_events _ftrace_events __stop_ftrace_events
__start_syscalls_metadata __syscalls_metadata __stop_syscalls_metadata
.devinit.rodata .cpuinit.rodata .meminit.rodata
__dtb_start .dtb.init.rodata __dtb_end
__setup_start .init.setup __setup_end
__initcall_start
.initcallearly.init
__initcall0_start .initcall0.init .initcall0s.init __initcall1_start .initcall1.init .initcall1s.init __initcall2_start .initcall2.init .initcall2s.init __initcall3_start .initcall3.init .initcall3s.init __initcall4_start .initcall4.init .initcall4s.init __initcall5_start .initcall5.init .initcall5s.init __initcallrootfs_start .initcallrootfs.init .initcall0rootfs.init __initcall6_start .initcall6.init .initcall6s.init __initcall7_start .initcall7.init .initcall7s.init 
__initcall_end
__con_initcall_start .con_initcall.init __con_initcall_end
__security_initcall_start .security_initcall.init __security_initcall_end
__initramfs_start .init.ramfs .init.ramfs.info

 *      PERCPU_SECTION(CACHELINE_SIZE) ->> __per_cpu_load
__per_cpu_start
.data..percpu..first
.data..percpu..page_aligned
.data..percpu..readmostly
.data..percpu
.data..percpu..shared_aligned
__per_cpu_end
 *      __init_end = .;
 *
 *      _stext = .;
 *      TEXT_SECTION = 0
 *      _etext = .;
 *
 *      _sdata = .;
 *      RO_DATA_SECTION(PAGE_SIZE) ->> __start_rodata
.rodata .rodata.*
__vermagic
__start___tracepoints_ptrs __tracepoints_ptrs __stop___tracepoints_ptrs
__tracepoints_strings
.rodata1
__start___bug_table __bug_table __stop___bug_table
__start_pci_fixups_early .pci_fixup_early __end_pci_fixups_early
__start_pci_fixups_header .pci_fixup_header __end_pci_fixups_header
__start_pci_fixups_final .pci_fixup_final __end_pci_fixups_final
__start_pci_fixups_enable .pci_fixup_enable __end_pci_fixups_enable
__start_pci_fixups_resume .pci_fixup_resume __end_pci_fixups_resume
__start_pci_fixups_resume_early .pci_fixup_resume_early __end_pci_fixups_resume_early
__start_pci_fixups_suspend .pci_fixup_suspend __end_pci_fixups_suspend
__start_builtin_fw .builtin_fw __end_builtin_fw
__start_rio_switch_ops .rio_switch_ops __end_rio_switch_ops
__tracedata_start .tracedata __tracedata_end
__start___ksymtab ___ksymtab+* __stop___ksymtab
__start___ksymtab_gpl ___ksymtab_gpl+* __stop___ksymtab_gpl
__start___ksymtab_unused ___ksymtab_unused+* __stop___ksymtab_unused
__start___ksymtab_unused_gpl ___ksymtab_unused_gpl+* __stop___ksymtab_unused_gpl
__start___ksymtab_gpl_future ___ksymtab_gpl_future+* __stop___ksymtab_gpl_future
__start___kcrctab ___kcrctab+* __stop___kcrctab
__start___kcrctab_gpl ___kcrctab_gpl+* __stop___kcrctab_gpl
__start___kcrctab_unused ___kcrctab_unused+* __stop___kcrctab_unused
__start___kcrctab_unused_gpl ___kcrctab_unused_gpl+* __stop___kcrctab_unused_gpl
__start___kcrctab_gpl_future ___kcrctab_gpl_future+* __stop___kcrctab_gpl_future
__ksymtab_strings
devinit.rodata
devexit.rodata
cpuinit.rodata
cpuexit.rodata
meminit.rodata
memexit.rodata
__start___param __param __stop___param
__start___modver __modver __stop___modver
__end_rodata
 *      RW_DATA_SECTION(...) ->> .data..init_task
__nosave_begin
.data..nosave
__nosave_end
.data..page_aligned
.data..cacheline_aligned
.data..read_mostly
.data
.ref.data
.data..shared_aligned
.devinit.data
.devexit.data
.cpuinit.data
.cpuexit.data
.meminit.data
.memexit.data
.data.unlikely
__tracepoints
__start___jump_table __jump_table __stop___jump_table
__start___verbose __verbose __stop___verbose
__start_annotated_branch_profile _ftrace_annotated_branch __stop_annotated_branch_profile
__start_branch_profile _ftrace_branch __stop_branch_profile
__start___trace_bprintk_fmt __trace_printk_fmt __stop___trace_bprintk_fmt
 *      _edata = .;
 *
 *      EXCEPTION_TABLE(...) ->> __start___ex_table __ex_table __stop___ex_table
 *      NOTES ->> __start_notes .note.* __stop_notes
 *
 *      BSS_SECTION(0, 0, 0) ->> __bss_start
.sbss .scommon
.bss..page_aligned .dynbss .bss COMMON
__bss_stop
 *      _end = .;
 *
 *      STABS_DEBUG ->> .stab .stabstr .stab.excl .stab.exclstr .stab.index .stab.indexstr .comment
 *      DWARF_DEBUG ->> .debug .line .debug_srcinfo .debug_sfnames .debug_aranges .debug_pubnames .debug_info.gnu.linkonce.wi.* \
.debug_abbrev .debug_line .debug_frame .debug_str .debug_loc .debug_macinfo \
.debug_weaknames .debug_funcnames .debug_typenames .debug_varnames
 *
 *      DISCARDS                // must be the last
->> .exit.text
devexit.text
cpuexit.text
memexit.text
.exit.data
devexit.data
devexit.rodata
cpuexit.data
cpuexit.rodata
memexit.data
memexit.rodata
.exitcall.exit
.discard
.discard.*
 * }