Subject: New page tables...

From: "Chagas, Jason" <[email-masked]>
Date: Thu, 2 Sep 1999 13:47:09 -0700

I've experienced an interesting problem trying to port ARM-Linux v2.2.3
(Brutus) to a new platform and StrongARM based processor.

The problem occurs when loading an executable image(/sbin/init - the first
executable called by the kernel). In the process of loading the executable,
the kernel calls 'new_page_tables' (memory.c). This function in turn calls
'pgd_alloc' (pgtable.h) to allocate a new translation table that is
eventually activated with CP15 (register 2) by a macro defined as
'SET_PAGE_DIR' (pgtable.h) followed by all the appropriate cache flushes.
The pointer to this new is saved is saved in task structure. Why is it that
each task needs its own copy of the translation table is unclear to me. I
would appreciate if someone could shed some light into it.

Anyway,  'pgd_alloc' in this case calls 'get_pgd_slow' (fault-armv.c).
'get_pgd_slow' allocates 16k of memory, zeros it out, and copies the
original translation table into this new buffer. However, it starts copying
the entries at the RAM address offset (0xc00000000 for Brutus or 0x20000000
for my board). Note, unlike the Brutus code that mapped all IO to virtual
address higher than RAM (mm-sa1100.c), I [inadvertently] mapped some IO
virtual address below the RAM address. Evidently, the end result was that my
IO addresses didn't get copied and the kernel hanged right after flushing of
the instruction and data TLBs ('_saxxxx_flsu_tlb_all' - proc-saxxxx.S). What
seems strange to me is that the Brutus kernel does not seem copy the
exception vector table entry, virtual address 0x0000000 initialized by
function setup_pagetables (mm_init.h), from the old translation table into
the new one. Some how the Brutus kernel still works. Could anyone explain to
me this magic?

I tried copying all (16k) the translation table (instead of just part of it)
into the new table and the kernel didn't hang after the TLB flush but it
"bombed" later with:

Bad pmd in pte_alloc: 03f00442

Function entered at [<200102c0>] from [<2002058c>]

  r4 = 00000000

Function entered at [<20020448>] from [<20035590>]

  r8 = 03ffffda

  r7 = 21fdde10
.
.
.

Any ideas?

Thanks,

Jason

unsubscribe: body of `unsubscribe linux-arm' to [email-masked]

---

From: Philip Blundell <[email-masked]>
Date: Thu, 02 Sep 1999 23:19:37 +0100

>each task needs its own copy of the translation table is unclear to me.

Each task might have different memory mappings and the easiest way to cope 
with this is to give each one a completely separate page table.  It would be 
possible to have a common L1 table and swap around the entries in it but this 
would add complexity for fairly little gain.

>for my board). Note, unlike the Brutus code that mapped all IO to virtual

Why have you mapped the kernel RAM so low down?  This will seriously reduce 
your available address space for user tasks, especially if you have I/O at 
even smaller addresses.  Unless there is a particular reason to do otherwise 
you probably want to keep the identity mapping of RAM at 0xc0000000.

>function setup_pagetables (mm_init.h), from the old translation table into

Doesn't get_pgd_slow do this?

>into the new table and the kernel didn't hang after the TLB flush but it

I suspect that copying the (now stale) user mappings may bring you to grief.
You want to copy just the kernel and I/O segments.

p.

---

From: [email-masked]
Date: Thu, 2 Sep 1999 23:33:45 +0100 (BST)

Chagas, Jason writes:
> Why is it that each task needs its own copy of the translation table is
> unclear to me. I would appreciate if someone could shed some light into it.

Every separate task in Linux requires its own set of page tables.  This
allows different user-space mappings between the virtual and physical
addresses to be set up.  When the kernel needs to switch between two
tasks, all it essentially needs to do is to remap the user space, and
the way this is done is to switch the page tables.

> However, it starts copying the entries at the RAM address offset
> (0xc00000000 for Brutus or 0x20000000 for my board). Note, unlike
> the Brutus code that mapped all IO to virtual address higher than
> RAM (mm-sa1100.c), I [inadvertently] mapped some IO virtual address
> below the RAM address.

Why are you setting this so low?  You're not leaving very much for the
actual user-mode tasks/threads.  Do you have a lot of IO space on this
board?  If not, then you might as well adopt the already proven generic
structure which is used on all the other 32-bit platforms.

There should be *NO* IO mappings below the TASK_SIZE boundary, except for
the first page.  TASK_SIZE defines the end of the 'user-mode' memory area.
Everything above that is defined to be 'system' mappings, which are
always copied.

> What seems strange to me is that the Brutus kernel does not seem copy the

See new_page_tables() in mm/memory.c.

> into the new table and the kernel didn't hang after the TLB flush but it

It will do - the kernel demand-loads the binaries, which means that it
relies on the page tables being marked as invalid to cause prefetch and
data aborts.  These aborts then cause pages to be read in.  If old pages
are present, then literally anything can happen to the user program, and
will probably result in the kernel killing the program in some way.

If you follow the generic mapping (zero-page, user, kernel, io in that
order) then you can't go far wrong with the page tables - it should
just work.

A tip - make sure 'Split text into discardable sections' is disabled.
It does cause strange problems at run time with most (but not all) of
the GNU ARM tool chain.  The latest versions may be ok, but at this
stage, I'd say you want that option off.
   _____
  |_____| ------------------------------------------------- ---+---+-
  |   |        Russell King       [email-masked]      --- ---
  | | | |  http://www.arm.linux.org.uk/~rmk/armlinux.html    /  /  |
  | +-+-+                                                     --- -+-
  /   |               THE developer of ARM Linux              |+| /|\
 /  | | |                                                     ---  |
    +-+-+ -------------------------------------------------  /\\\  |
unsubscribe: body of `unsubscribe linux-arm' to [email-masked]