<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4501010079801219353</id><updated>2012-02-16T11:19:28.991-08:00</updated><title type='text'>Tech stuff.</title><subtitle type='html'>Tech stuff contains posts related to systems programming, linux, and processor architectures.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://pankaj-techstuff.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4501010079801219353/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://pankaj-techstuff.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>pankaj</name><uri>http://www.blogger.com/profile/08680566769870965457</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://2.bp.blogspot.com/_WVIQQbV4zbI/SN2tSwbzrUI/AAAAAAAAAJE/eAJsqxSCYoo/S220/Picture+344.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>2</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4501010079801219353.post-3354314927316141754</id><published>2007-12-14T01:28:00.000-08:00</published><updated>2008-11-21T09:52:30.789-08:00</updated><title type='text'>Arm MMU in linux: page table Initialization and tweaks for integeration with Memory management code.</title><content type='html'>&lt;div&gt;&lt;span style="font-family:arial;"&gt;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. &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;Following conventions are used in this writeup:&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;All 'C' function names are in &lt;em&gt;italics.&lt;/em&gt;&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;All 'Assembly' function names and lables are metioned in &lt;strong&gt;&lt;em&gt;bold italics&lt;/em&gt;&lt;/strong&gt;.&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;No file names will be mentioned here (with exception of assembly files), use ctags.&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;All details are for uniprocessor system, nothing related to SMP is covered here.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-family:arial;"&gt;Kernel's execution starts at &lt;strong&gt;&lt;em&gt;stext&lt;/em&gt;&lt;/strong&gt; 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 &lt;strong&gt;&lt;em&gt;__create_page_tables &lt;/em&gt;&lt;/strong&gt;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: &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:arial;"&gt;-----------------------------&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;_create_page_tables:  &lt;/span&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;ldr r5, [r8, #MACHINFO_PHYSRAM] @ physram&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;pgtbl r4, r5 @ page table address&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;mov r0, r4&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;mov r3, #0&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;add r6, r0, #0x4000&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;1: str r3, [r0], #4&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;str r3, [r0], #4&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;str r3, [r0], #4&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;str r3, [r0], #4&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;teq r0, r6&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;bne 1b&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;ldr r7, [r10, #PROCINFO_MMUFLAGS] @ mmuflags&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;mov r6, pc, lsr #20 @ start of kernel section&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;orr r3, r7, r6, lsl #20 @ flags + kernel base&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;str r3, [r4, r6, lsl #2] @ identity mapping&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;add r0, r4, #(TEXTADDR &amp;amp; 0xff000000) &gt;&gt; 18 @ start of kernel&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;str r3, [r0, #(TEXTADDR &amp;amp; 0x00f00000) &gt;&gt; 18]!&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;add r3, r3, #1 &lt;&lt;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;str r3, [r0, #4]! @ KERNEL + 1MB&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;add r3, r3, #1 &lt;&lt;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;str r3, [r0, #4]! @ KERNEL + 2MB&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;add r3, r3, #1 &lt;&lt;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;str r3, [r0, #4] @ KERNEL + 3MB &lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;p&gt;&lt;span style=";font-family:arial;font-size:85%;"  &gt;-----------------------------&lt;/span&gt;&lt;/p&gt;&lt;span style=";font-family:arial;font-size:100%;"  &gt;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.e &lt;strong&gt;&lt;em&gt;stext&lt;/em&gt;&lt;/strong&gt;). 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)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=";font-family:arial;font-size:100%;"  &gt;so the initial memory map looks something line this:&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;img id="BLOGGER_PHOTO_ID_5162284284724105954" style="margin: 0px auto 10px; display: block; width: 345px; height: 187px; text-align: center;" alt="" src="http://2.bp.blogspot.com/_WVIQQbV4zbI/R6QeNLyP0uI/AAAAAAAAAHo/U46zp2hBJ48/s200/initmap.bmp" border="0" /&gt;&lt;span style="font-size:85%;"&gt;After the initial page tables are setup, next step is to enable MMU. This code is tricky, does lot of deep magic. &lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-size:85%;"&gt;----------------------------&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;ldr r13, __switch_data @ address to jump to after mmu has been enabled&lt;br /&gt;&lt;/li&gt;&lt;li&gt;adr lr, __enable_mmu @ return (PIC) address&lt;br /&gt;&lt;/li&gt;&lt;li&gt;add pc, r10, #PROCINFO_INITFUNC&lt;/li&gt;&lt;li&gt;&lt;br /&gt;.type __switch_data, %object&lt;/li&gt;&lt;br /&gt;&lt;li&gt;__switch_data: .long __mmap_switched&lt;br /&gt;&lt;/li&gt;&lt;li&gt;.long __data_loc @ r4&lt;br /&gt;&lt;/li&gt;&lt;li&gt;.long __data_start @ r5&lt;br /&gt;&lt;/li&gt;&lt;li&gt;.long __bss_start @ r6&lt;br /&gt;&lt;/li&gt;&lt;li&gt;.long _end @ r7&lt;br /&gt;&lt;/li&gt;&lt;li&gt;.long processor_id @ r4&lt;br /&gt;&lt;/li&gt;&lt;li&gt;.long __machine_arch_type @ r5&lt;br /&gt;&lt;/li&gt;&lt;li&gt;.long cr_alignment @ r6&lt;br /&gt;&lt;/li&gt;&lt;li&gt;.long init_thread_union+8192 @ sp  &lt;/li&gt;&lt;/ol&gt;&lt;span style="font-size:85%;"&gt;-----------------------------&lt;/span&gt;&lt;br /&gt;&lt;p&gt;&lt;span style="font-size:85%;"&gt;&lt;/span&gt;&lt;/p&gt;&lt;span style="font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;p&gt;line #1 puts virtual address of &lt;strong&gt;&lt;em&gt;__mmap_switched&lt;/em&gt;&lt;/strong&gt; 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 &lt;strong&gt;&lt;em&gt;__mmap_switched. &lt;/em&gt;&lt;/strong&gt;And now since in &lt;em&gt;&lt;strong&gt;__mmap_switched&lt;/strong&gt;&lt;/em&gt; we start refering to virtual addresses of variables and functions, so starting from &lt;em&gt;&lt;strong&gt;__mmap_switched&lt;/strong&gt; &lt;/em&gt;is no longer position independent.&lt;/p&gt;At line #2-#3, we put position independent address of &lt;strong&gt;&lt;em&gt;__enable_mmu &lt;/em&gt;&lt;/strong&gt;('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 &lt;strong&gt;&lt;em&gt;__v6_proc_info&lt;/em&gt;&lt;/strong&gt; we have a branch to &lt;strong&gt;&lt;em&gt;__v6_setup, &lt;/em&gt;&lt;/strong&gt;which does following setup for enabling MMU:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Clean and Invalidate D-cache and Icache, and invalidate TLB.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;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.&lt;/li&gt;&lt;/ul&gt;As &lt;strong&gt;&lt;em&gt;__v6_setup &lt;/em&gt;&lt;/strong&gt;returns we enter &lt;strong&gt;&lt;em&gt;__enable_mmu&lt;/em&gt;&lt;/strong&gt;, 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 &lt;strong&gt;&lt;em&gt;__v6_setup&lt;/em&gt;&lt;/strong&gt;) and then calls &lt;strong&gt;&lt;em&gt;__turn_mmu_on.&lt;/em&gt;&lt;/strong&gt;&lt;p&gt; &lt;/p&gt;--------------------------------------&lt;br /&gt;&lt;p&gt;&lt;span style="font-family:arial;"&gt;__turn_mmu_on:&lt;/span&gt;&lt;/p&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;mov r0, r0&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;mcr p15, 0, r0, c1, c0, 0 @ write control reg&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;mrc p15, 0, r3, c0, c0, 0 @ read id reg&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;mov r3, r3&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;mov r3, r3&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;mov pc, r13&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;--------------------------------------&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;strong&gt;&lt;em&gt;&lt;br /&gt;__turn_mmu_on &lt;/em&gt;&lt;/strong&gt;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 &lt;strong&gt;&lt;em&gt;__mmap_switched.&lt;/em&gt;&lt;/strong&gt; 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 by &lt;strong&gt;&lt;em&gt;paging_init&lt;/em&gt;&lt;/strong&gt; and mappings created by &lt;strong&gt;&lt;em&gt;__create_page_tables&lt;/em&gt;&lt;/strong&gt; will be discarded), we are still running with 4Mb mapping that &lt;strong&gt;&lt;em&gt;__create_page_tables &lt;/em&gt;&lt;/strong&gt;had set it for us. &lt;strong&gt;&lt;em&gt;__mmap_switched &lt;/em&gt;&lt;/strong&gt;copies the data segment if required, clears the BSS and calls &lt;strong&gt;&lt;em&gt;start_kernel. &lt;/em&gt;&lt;/strong&gt;&lt;/span&gt;&lt;p&gt;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&lt;span style="font-style: italic;"&gt;&lt;span style="font-weight: bold;"&gt; &lt;/span&gt;paging_init( ) &lt;/span&gt;( &lt;span style="font-style: italic;"&gt;&lt;span style="font-style: italic;"&gt;&lt;/span&gt;start_kernel( ) -&gt; setup_arch( )-&gt; paging_init( ))&lt;/span&gt;.&lt;/p&gt;&lt;p&gt;&lt;span style="font-style: italic;"&gt;paging_init() &lt;/span&gt;populates the master L1 page table (&lt;span style="font-style: italic;"&gt;init_mm) &lt;/span&gt;with linear mappings of complete SDRAM and the SOC specific address space (SOC specific memory mappings are created by &lt;span style="font-style: italic;"&gt;mdesc-&gt;mapio() &lt;/span&gt;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 &lt;span style="font-style: italic;"&gt;mdesc-&gt;mapio() &lt;/span&gt;function.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-size:100%;"&gt;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.&lt;/span&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Handling of mapings for VMALLOC REGION are is bit tricky, because VMALLOC virtual addressed are allocated when a process calls &lt;span style="font-style: italic;"&gt;vmalloc( ). &lt;/span&gt;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.&lt;br /&gt;&lt;/p&gt;&lt;strong&gt;Tweaks for integerating ARM 2 level page tables with linux implementation of 3 level ix86 style page tables&lt;/strong&gt;&lt;br /&gt;&lt;p&gt;&lt;strong&gt;1. &lt;/strong&gt;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 &lt;strong&gt;&lt;em&gt;__pmd &lt;/em&gt;&lt;/strong&gt;is defined as a identity macro in (include/asm-arm/page.h): &lt;/p&gt;-------------------------------------------------&lt;br /&gt;&lt;p&gt;#define __pmd(x) ((pmd_t) { (x) } )&lt;/p&gt;--------------------------------------------------&lt;br /&gt;&lt;p&gt;So effectively for ARM, linux is told that pmd has just one entry, effectively bypassing pmd.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;span style="font-weight: bold;"&gt;2&lt;/span&gt;. 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).&lt;br /&gt;&lt;span style="font-size:100%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-size:100%;"&gt;&lt;a href="http://lxr.linux.no/linux+v2.6.26.5/include/asm-arm/pgtable.h#L65" id="L65" class="line" name="L65"&gt;&lt;/a&gt;&lt;/span&gt;                                                           &lt;span style="font-size:100%;"&gt;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 &lt;a href="http://lxr.linux.no/linux+v2.6.11/include/asm-arm/pgtable.h"&gt;include/asm-arm/pgtable.h&lt;/a&gt; (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.&lt;br /&gt;&lt;br /&gt;So here we conclude the architecture specific page table related stuff for ARM.&lt;/span&gt;&lt;br /&gt;&lt;p&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;&lt;/span&gt;&lt;/p&gt;&lt;span style="font-size:85%;"&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4501010079801219353-3354314927316141754?l=pankaj-techstuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pankaj-techstuff.blogspot.com/feeds/3354314927316141754/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4501010079801219353&amp;postID=3354314927316141754' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4501010079801219353/posts/default/3354314927316141754'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4501010079801219353/posts/default/3354314927316141754'/><link rel='alternate' type='text/html' href='http://pankaj-techstuff.blogspot.com/2007/12/initialization-of-arm-mmu-in-linux.html' title='Arm MMU in linux: page table Initialization and tweaks for integeration with Memory management code.'/><author><name>pankaj</name><uri>http://www.blogger.com/profile/08680566769870965457</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://2.bp.blogspot.com/_WVIQQbV4zbI/SN2tSwbzrUI/AAAAAAAAAJE/eAJsqxSCYoo/S220/Picture+344.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_WVIQQbV4zbI/R6QeNLyP0uI/AAAAAAAAAHo/U46zp2hBJ48/s72-c/initmap.bmp' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4501010079801219353.post-6910092544453343899</id><published>2007-11-16T07:15:00.000-08:00</published><updated>2007-11-17T02:01:58.120-08:00</updated><title type='text'>The story Interrupt handling in linux 2.6.11 on ARM.</title><content type='html'>&lt;p&gt;&lt;span style="font-family:arial;"&gt;This post explains how the interrupts are handled in linux kernel, what homework kernel has to do before first interrupt is recieved. We took 2.6.11 kernel as reference, and for architecture specific details we used a ARM11 based devlopment board.&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:arial;"&gt;Following conventions are used in this writeup:&lt;/span&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-family:Arial;"&gt;All 'C' function names are in &lt;em&gt;italics. &lt;/em&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:Arial;"&gt;All 'Assembly' function names and lables are metioned in &lt;strong&gt;&lt;em&gt;bold italics&lt;/em&gt;&lt;/strong&gt;.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:Arial;"&gt;No file names will be mentioned here (with exception of assembly files), use ctags. &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:Arial;"&gt;All details are for uniprocessor system, nothing related to SMP is covered here.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:Arial;"&gt;Any other convention - ???? :) NO.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;span style="font-family:arial;"&gt;We'll cover the whole interrupt stuff in two sections:&lt;/span&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Interrupt setup - Explanation of Generic and architecture specific setup that kernel does.&lt;/span&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Interrupt handling&lt;/span&gt; - Explanation of what happens after processor recieves an interrupt.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;span &gt;&lt;strong&gt;1. Interrupt &lt;/strong&gt;&lt;/span&gt;&lt;em&gt;&lt;span &gt;&lt;strong&gt;setup&lt;/strong&gt;&lt;/span&gt; &lt;/em&gt;&lt;/p&gt;&lt;p&gt;&lt;em&gt;s&lt;span style="font-family:arial;"&gt;tart_kernel( &lt;/span&gt;&lt;/em&gt;&lt;span style="font-family:arial;"&gt;) is the first 'C' function that opens its eyes when kernel is booting up. It intializes various subsystems of the kernel, including IRQ system. Intialization of IRQ requires that you have valid vector table in place and you have first level interrupt hadlers in place, both of these things are architecture specifc. Lets setup the vector table first. &lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:arial;"&gt;&lt;em&gt;start_kernel( ) &lt;/em&gt;calls a function called &lt;em&gt;trap_init( ) &lt;/em&gt;which does following:&lt;/span&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Call &lt;strong&gt;&lt;em&gt;__trap_init()&lt;/em&gt;  &lt;/strong&gt;to setup exception vector table at location 0xffff0000. &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:arial;"&gt;Flush the icache in range 0xffff0000 to 0xffff0000 + PAGE_SIZE. This is required because &lt;em&gt;&lt;strong&gt;__trap_init( )&lt;/strong&gt; &lt;/em&gt;moves the vector table and vector stubs to 0xffff0000.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;span style="font-family:arial;"&gt;Vector table and vector stub code for ARM resides in arch/arm/kernel/entry-armv.S file. In this file you'll find implementaion of &lt;strong&gt;&lt;em&gt;__trap_init( ) &lt;/em&gt;&lt;/strong&gt;funtion. Following is a code snippet from  entry-armv.S, we'll go in details of this code:&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family:arial;"&gt;----------------------------------------------------&lt;/span&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt;.equ    __real_stubs_start, .LCvectors + 0x200&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt;.LCvectors:&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt;swi SYS_ERROR0&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt;b   __real_stubs_start + (vector_und - __stubs_start)&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt;ldr pc, __real_stubs_start + (.LCvswi - __stubs_start)&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt;b   __real_stubs_start + (vector_pabt - __stubs_start)&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt;b   __real_stubs_start + (vector_dabt - __stubs_start)&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt;b   __real_stubs_start + (vector_addrexcptn - __stubs_start)&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt; b   __real_stubs_start + (vector_irq - __stubs_start)&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt; b   __real_stubs_start + (vector_fiq - __stubs_start)&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt;ENTRY(__trap_init) &lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt;stmfd   sp!, {r4 - r6, lr}&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt;mov r0, #0xff000000   &lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt; orr r0, r0, #0x00ff0000     @ high vectors position&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt;adr r1, .LCvectors          @ set up the vectors &lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt;ldmia   r1, {r1, r2, r3, r4, r5, r6, ip, lr}&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt;stmia   r0, {r1, r2, r3, r4, r5, r6, ip, lr}&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt;add r2, r0, #0x200  &lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt; adr r0, __stubs_start       @ copy stubs to 0x200 &lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt;adr r1, __stubs_end&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt;&lt;strong&gt;1:&lt;/strong&gt;  ldr r3, [r0], #4  &lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt;  str r3, [r2], #4 &lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt;  cmp r0, r1   &lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;li&gt;&lt;div align="left"&gt;&lt;span style="font-family:times new roman;"&gt; blt 1b &lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p align="left"&gt;&lt;span style="font-family:times new roman;"&gt; LOADREGS(fd, sp!, {r4 - r6, pc})&lt;/span&gt;&lt;/p&gt;&lt;p align="left"&gt;&lt;span style="font-family:arial;"&gt;-------------------------------------------------------------&lt;br /&gt;In the code snippet given above vector table is between line 4- 10. As we can see this vector contains branch instruction for branching to exception handler code which also resides in same file (arm-entryV.S).  Vector table contains the branch instructions for all the exceptions defined in ARM (Undefined instruction, SWI, data abort, prefecth abort, IRQ, and FIQ). The most important thing to note about this vector table is that branch instructions are used for all exceptions except SWI. Using branch instruction instead of loading PC directly with the exceptions handler address makes this code position independent. Since branch instruction take offset (+ive or -ve) from current PC, this code will run fine  as long as the offset between vector table instructions and the exception handlers is maintained as desired by this code. And It is assumed here that exception handlers will be at +0x200 offset from starting address of vector table. &lt;/span&gt;&lt;/p&gt;&lt;p align="left"&gt;&lt;span style="font-family:arial;"&gt;&lt;strong&gt;&lt;em&gt;__trap_init( ) &lt;/em&gt;&lt;/strong&gt;function copies the vector table at location 0xffff0000 (line 13-17) and copies the exception handlers at 0xffff0200 (line 18-24).  Remember that addresses  we are talking about here are virtual addresses. &lt;/span&gt;&lt;/p&gt;&lt;p align="left"&gt;&lt;span style="font-family:arial;"&gt;So we are done with setting up vector tables and the exception handlers. If you want then you can hook your exception handler directly to the vector table, so that you bypass all linux interrupt handling code, which is pretty heavy. But if you do so then you'll have to get your hands dirty with all the architecture details which kernel handles beautifully and cleanly.&lt;/span&gt;&lt;/p&gt;&lt;p align="left"&gt;&lt;span style="font-family:arial;"&gt;After setting up vector tables &lt;em&gt;start_kernel ( ) &lt;/em&gt;calls &lt;em&gt;init_IRQ() &lt;/em&gt;to set up kernel IRQ handling infrastructure, on ARM we have 32 hard interrupts  for which kernel kernel sets up the a default desctiptor called &lt;em&gt;bad_irq_desc, &lt;/em&gt;which has &lt;em&gt;do_bad_IRQ( ) &lt;/em&gt;as IRQ handler. Then &lt;em&gt;init_IRQ( )&lt;/em&gt; calls &lt;em&gt;init_arch_irq( ), &lt;/em&gt;here the architecture specfic code has to setup the IRQ handlers for 32 IRQs, if not set then &lt;em&gt;do_bad_IRQ( ) &lt;/em&gt;will handle the IRQs. On our reference architecture we setup &lt;em&gt;do_level_IRQ( ) &lt;/em&gt;as IRQ handlers of all the IRQs except the system timer IRQ. This is the second place where you can bypass kernel IRQ handlers and hook your IRQ handler directly. If you do'nt have requirement of hooking your IRQ handler here then just let &lt;em&gt;do_level_IRQ( ) &lt;/em&gt;handle the IRQs then you can register your IRQ handlers with &lt;em&gt;request_irq() &lt;/em&gt;in traditional way, and kernel will call your IRQ handler whenever interrupt occurs hiding all the dirty arch details :)&lt;/span&gt;&lt;/p&gt;&lt;p align="left"&gt;&lt;span style="font-family:arial;"&gt;So now we have our IRQ infrastructure in place, and various modules can register thier IRQ handlers through &lt;em&gt;request_irq(). &lt;/em&gt;When you call &lt;em&gt;request_irq( )&lt;/em&gt;  kernel appends your IRQ handler to list of IRQ handlers registered for that particular IRQ line, it does not change the exception vector table. &lt;/span&gt;&lt;/p&gt;&lt;p align="left"&gt;&lt;span style="font-family:arial;"&gt;Now lets see what happens after interrupt is recieved. &lt;/span&gt;&lt;/p&gt;&lt;p align="left"&gt;&lt;strong&gt;&lt;span style="font-family:arial;"&gt;2. Interrupt Handling&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p align="left"&gt;&lt;span style="font-family:Arial;"&gt;When a IRQ is raised, ARM stops what it is processing ( Asuming it is not processing a FIQ!), disables further IRQs (not FIQs), puts CPSR in SPSR, puts current PC to LR and swithes to IRQ mode, refers to the vector table and jumps to the exception handler. In our case it jumps to the exception handler of IRQ. following is the snippet of code for exception handler code for IRQ (again from entry-armV.S file):&lt;/span&gt;&lt;/p&gt;&lt;p align="left"&gt;&lt;span style="font-family:Arial;"&gt;------------------------------------------------------------------------&lt;/span&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt;&lt;strong&gt;vector_irq&lt;/strong&gt;: &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt;ldr r13, .LCsirq &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt;.if \correction    &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt;sub lr, lr, #\correction &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt; .endif  &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt;str lr, [r13]           @ save lr_IRQ&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt; mrs lr, spsr    &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt;str lr, [r13, #4]           @ save spsr_IRQ   &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt;    mrs r13, cpsr   &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt; bic r13, r13, #MODE_MASK  &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt;orr r13, r13, #MODE_SVC   &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt; msr spsr_cxsf, r13          @ switch to SVC_32 mode &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt; and lr, lr, #15    &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt;ldr lr, [pc, lr, lsl #2]   &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt; movs    pc, lr              @ Changes mode and branches &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt;.long   __irq_usr           @  0  (USR_26 / USR_32) &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt; .long   __irq_invalid           @  1  (FIQ_26 / FIQ_32) &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt;.long   __irq_invalid           @  2  (IRQ_26 / IRQ_32) &lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:times new roman;"&gt;.long   __irq_svc           @  3  (SVC_26 / SVC_32) &lt;/span&gt;   &lt;strong&gt;&lt;span style="font-family:Arial;"&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p align="left"&gt;&lt;strong&gt;&lt;span style="font-family:times new roman;"&gt;--------------------------------------------------------------------------&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p align="left"&gt;&lt;span style="font-family:arial;"&gt;In the above snipped a macro called &lt;em&gt;&lt;strong&gt;vector_stub&lt;/strong&gt; &lt;/em&gt;has been intensionally expanded to improve readability. So when ARM refers to the vector table it follows the branch and lands up at line 2. At this moment ARM is in IRQ mode, IRQs are disabled,  LR contains PC of when interrupt occured and SPSR contains CPSR of when interrupt occured. Since we are in IRQ mode so r13 (SP) is banked, so we load SP with address of a small stack frame that we have created at &lt;strong&gt;&lt;em&gt;__temp_irq&lt;/em&gt;&lt;/strong&gt; (.LCsirq contains address of&lt;em&gt;&lt;strong&gt; __temp_irq&lt;/strong&gt;&lt;/em&gt;). This stack is only used when we are are in IRQ mode.&lt;/span&gt;&lt;/p&gt;&lt;p align="left"&gt;&lt;span style="font-family:arial;"&gt;In lines 6-8 we save LR and SPSR on the temporary IRQ stack (&lt;em&gt;&lt;strong&gt;__temp_irq&lt;/strong&gt;&lt;/em&gt;). And  we switch to SVC mode (line 10-12). After this basic setup is done depending on the mode in which ARM was there when interrupt occured we switch to specific handler. We'll assume that ARM was executing in SVC mode, so we'll look ino the details of &lt;strong&gt;&lt;em&gt;__irq_svc &lt;/em&gt;&lt;/strong&gt;.&lt;/span&gt;&lt;/p&gt;&lt;p align="left"&gt;&lt;span style="font-family:Arial;"&gt;&lt;strong&gt;&lt;em&gt;__irq_svc &lt;/em&gt;&lt;/strong&gt;saves r0-12 on SVC mode stack (i.e kernel stack of process which was interrupted), reads LR and SPSR from temporary IRQ stack &lt;strong&gt;&lt;em&gt;(__temp_irq&lt;/em&gt;&lt;/strong&gt;) and saves them on SVC mode stack, increments the preemt count and then calls &lt;strong&gt;&lt;em&gt;get_irqnr_and_base &lt;/em&gt;&lt;/strong&gt;to find out the IRQ line number. Each architecutre has to provide implementation of &lt;strong&gt;&lt;em&gt;get_irqnr_and_base,&lt;/em&gt;&lt;/strong&gt;  in which it has to query the Interrupt Controller in ARCH specific way to find out which IRQ line raised this interrupt. &lt;/span&gt;&lt;/p&gt;&lt;p align="left"&gt;&lt;span style="font-family:Arial;"&gt;After doing all this &lt;strong&gt;&lt;em&gt;__irq_svc &lt;/em&gt;&lt;/strong&gt;calls &lt;em&gt;asm_do_IRQ(), &lt;/em&gt;Finally we are out of assembly code, now life will be simpler :). And after &lt;em&gt;asm_do_IRQ returns &lt;strong&gt;__irq_svc &lt;/strong&gt;&lt;/em&gt;will restore the state of process which was interrupted.&lt;em&gt; &lt;/em&gt;&lt;/span&gt;&lt;/p&gt;&lt;p align="left"&gt;&lt;span style="font-family:Arial;"&gt;&lt;em&gt;asm_do_IRQ( ) &lt;/em&gt;just calls the IRQ handler that was registered by architecure code (refer to section 1), in our case we had set &lt;em&gt;do_level_irq( ) &lt;/em&gt;as interrupt handler for all IRQs except timer IRQ. so for all IRQs except timer &lt;em&gt;do_level_irq( ) &lt;/em&gt;will handle our interrupts. &lt;/span&gt;&lt;/p&gt;&lt;p align="left"&gt;&lt;span style="font-family:Arial;"&gt;&lt;em&gt;do_level_irq() &lt;/em&gt;first ACKs the interrupt, by calling architecure specific ACK function. On our reference architecture we just mask this interrupt line, which means that no IRQs of same IRQ line will be allowed until all the IRQ handlers have completed their job. After this &lt;em&gt;do_level_irq( ) &lt;/em&gt;checks whether we have any action registered for this IRQ (the interrupt handler that you register through &lt;em&gt;request_irq ( ) &lt;/em&gt;are called actions). If there is an action registered then &lt;em&gt;__do_irq( ) &lt;/em&gt;is called which in trun enables the interrupts (remember ARM had disabled it) except current IRQ line, and executes the actions. &lt;/span&gt;&lt;/p&gt;&lt;p align="left"&gt;&lt;span style="font-family:Arial;"&gt;After all actions have completed excecuting, IRQ line for which interrupt was raised is unmasked and &lt;em&gt;do_level_irq() &lt;/em&gt;returns. After this interrupt handling is complete&lt;strong&gt;&lt;em&gt;  __irq_svc&lt;/em&gt;&lt;/strong&gt;  restores the state of interrupted process and that process lives happily until another interrupt bugs it again :)&lt;/span&gt;&lt;/p&gt;&lt;p align="left"&gt;That was the overview of interrupt handling in linux on ARM, we hope it was useful :)&lt;/p&gt;&lt;p align="left"&gt;- Pankaj And Sripurna. &lt;/p&gt;&lt;p align="left"&gt;&lt;span style="font-family:Arial;"&gt;&lt;/span&gt; &lt;/p&gt;&lt;p align="left"&gt; &lt;/p&gt;&lt;p align="left"&gt; &lt;/p&gt;&lt;p align="left"&gt; &lt;/p&gt;&lt;p align="left"&gt;&lt;span style="font-family:Arial;"&gt;&lt;/span&gt; &lt;/p&gt;&lt;p align="left"&gt;&lt;span style="font-family:Arial;"&gt;&lt;/span&gt; &lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4501010079801219353-6910092544453343899?l=pankaj-techstuff.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pankaj-techstuff.blogspot.com/feeds/6910092544453343899/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=4501010079801219353&amp;postID=6910092544453343899' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4501010079801219353/posts/default/6910092544453343899'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4501010079801219353/posts/default/6910092544453343899'/><link rel='alternate' type='text/html' href='http://pankaj-techstuff.blogspot.com/2007/11/story-interrupt-handling-in-linux-2611.html' title='The story Interrupt handling in linux 2.6.11 on ARM.'/><author><name>pankaj</name><uri>http://www.blogger.com/profile/08680566769870965457</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://2.bp.blogspot.com/_WVIQQbV4zbI/SN2tSwbzrUI/AAAAAAAAAJE/eAJsqxSCYoo/S220/Picture+344.jpg'/></author><thr:total>2</thr:total></entry></feed>
