2016-10-06 5 views
0

Ich versuche, die Funktion Lookup-Adresse (http://lxr.free-electrons.com/source/arch/x86/mm/pageattr.c#L373) für Arm-Plattform (und nur für Kernel-Seitentabellen) zu emulieren.walkup Seitentabellen auf ARM

Der Punkt ist, ich bekomme die Adresse von swapper_pg_dir von TTBR1, und bis jetzt funktioniert das. Ich habe es mit GDB:

(gdb) file vmlinux 
Reading symbols from vmlinux...done. 
(gdb) p init_mm.pgd 
$1 = (pgd_t *) 0xc0004000 
(gdb) 

und der Code von meinem Modul:

static pgd_t *get_global_pgd (void) 

{ 
     pgd_t *pgd; 
     unsigned int ttb_reg; 

     asm volatile (
     "  mrc  p15, 0, %0, c2, c0, 1" 
     : "=r" (ttb_reg)); 

     ttb_reg &= TTBR_MASK; 
     pgd = __va (ttb_reg); 
     pr_info ("get_global_pgd: %p\n", pgd); 

     return pgd; 
} 

und die Ausgabe:

bananapi kernel: [ 5665.358139] mod: get_global_pgd: c0004000 

Bisher ist dies passend. Jetzt bin ich die Berechnung Adr des rechten pgd, tun:

pgd = get_global_pgd() + pgd_index (addr); 

Und da (Adr >> 21) ist 0x600, ich 0xc0007000. Dann fahre ich fort mit:

pud = pud_offset (pgd, addr); 
pr_info ("pud: 0x%0x - %p\n",pud_val (*pud), pud); 
pmd = pmd_offset (pud, addr); 
pr_info ("pmd: 0x%0x - %p\n", pmd_val (*pmd), pmd); 
if (pmd == NULL || pmd_none (*pmd)) { 
     return NULL; 
} 
return pte_offset_kernel (pmd, addr); 

Ausgang:

bananapi kernel: [ 5665.390391] mod: pud: 0x4001140e - c0007000 
bananapi kernel: [ 5665.401603] mod: pmd: 0x4001140e - c0007000 
bananapi kernel: [ 5665.423838] mod: pte: 0xe59f119c - c0011020 

Das Problem ist, dass die pte ich scheint nicht in Ordnung zu sein, weil die Attribute des pte nicht übereinstimmen. Lassen Sie uns eine Adresse nehmen von/proc/kallsyms:

c0008054 t __create_page_tables 

ich es mit GDB lesen:

(gdb) x/2x 0xc0008054 
0xc0008054 <__create_page_tables>: 0xe2884901 0xe1a00004 
(gdb) 

Aber die pte ich von dieser Adresse erhalten, müssen nicht die vorliegende Flagge:

ich es mit der Überprüfung (diese pte ist diejenige, die ich von meinem lookup_address bekam):

ret = pte_present (*pte); 
pr_info ("pte_present: %d\n", ret); 

Das pte_present ist 0 (was das Flag L_PTE_PRESENT prüft, das include/asm/pgtable-2level.h definiert ist), aber sollte nicht 0 sein, solange ich es in GDB lesen kann.

Ich habe mit einigen anderen Adressen, zum Beispiel getestet: 0xc0035618:

c0035618 T __put_task_struct 

Und für diesen einen großen der L_PTE_PRESENT gesetzt.

Ich bin mir ziemlich sicher, dass ich etwas vermisse, oder ich habe es falsch verstanden.

Vielen Dank im Voraus!

+0

Die 'L_ *' Attribute sind Linux-spezifische, nicht unbedingt Hardware-Attribute. Es gibt bereits viele Fragen dazu, wie ARM Linux [eine zweite Gruppe von Schatten-Seitentabellen für diese unterhält] (http://lxr.free-electronics.com/source/arch/arm/include/asm/pgtable-2level). h). – Notlikethat

+0

Ja, das weiß ich. Eigentlich denke ich, dass ARM diese Bits nicht zur Verfügung stellt, also musste Linux es umgehen, indem er diese Bits hinzufügte. Aber Linux prüft diese Bits, wenn ein Seitenfehler ausgelöst wird, also denke ich, dass sie die richtigen sind. Danke – leberus

+0

Mögliches Duplikat von [Page table entry (PTE) -Deskriptor im Linux-Kernel für ARM] (http://stackoverflow.com/questions/16909101/page-table-entry-pte-descriptor-in-linux-kernel-for-- Arm) –

Antwort

0

Ich habe alle spitzen Links gelesen, aber ich bin afraig ich bekomme immer noch nicht das ganze Bild. Ich versuche zu erklären, was ich bisher verstanden habe:

Von include/asm/pgtable-2level.h, es sieht aus wie eine Seite speichert:

  • 0 - pte1_linux
  • 1024 - pte2_linux
  • 2048 - pte1_hw
  • 3072 - pte2_hw

Eigentlich sehe ich dies auch in early_pte_alloc Funktion , die 4096 bytes für den pte zuweist:

 static pte_t * __init early_pte_alloc(pmd_t *pmd, unsigned long addr, unsigned long prot) 

    { 
     if (pmd_none(*pmd)) { 
       pte_t *pte = early_alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE); 
       __pmd_populate(pmd, __pa(pte), prot); 
     } 
     BUG_ON(pmd_bad(*pmd)); 
     return pte_offset_kernel(pmd, addr); 
} 

Dann nehmen wir in __pmd_populate die phys-Adresse des zuvor zugewiesenen Speichers, wir fügen 2048 (für hw pte) hinzu, und wir ODER es mit dem Schutz-Flag (was im Falle einer kernel_domain sollte PMD_TYPE_TABLE sein.

static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t pte, 
            pmdval_t prot) 
{ 
     pmdval_t pmdval = (pte + PTE_HWTABLE_OFF) | prot; 
     pmdp[0] = __pmd(pmdval); 
#ifndef CONFIG_ARM_LPAE 
     pmdp[1] = __pmd(pmdval + 256 * sizeof(pte_t)); 
#endif 
     flush_pmd_entry(pmdp); 
} 

Bis jetzt ist klar. diese Informationen gegeben, sollte eine wandelnde Seite so etwas wie:

  • pmd_offset_k (adr)
  • pud_offset (PGD, adr)
  • pmd_offset (PUD, adr)
  • pte_offset_kernel (pmd, adr)

pte_offset_kernel gibt die virtuelle Adresse des pmd_val in pmd gespeichert. (es teilt auch den Wert mit PHYS_MASK und PAGE_MASK AN) und fügt den pte_index (addr) hinzu. An diesem Punkt sollte ich den Wert der virtuellen Adressen von linux_pte_0 haben (weil die vorherigen ANDs mit PAGE_MASK mich an den Anfang der Seite gebracht haben).

Also ich denke an diesem Punkt sollte ich in der Lage sein, die L_PTE_ * Flags zu überprüfen.

Bin ich falsch?

Vielen Dank im Voraus