2001-10-04 01:10:30

by Ian Thompson

[permalink] [raw]
Subject: How can I jump to non-linux address space?

Hi all,

I'm sorry if this is off-topic, but I wasn't sure where else to ask...

My kernel is running from RAM, and I want to jump to an address in ROM
(which unfortunately, the kernel doesn't seem to know anything about). I
don't plan on trying to resume the kernel after doing this. However, I'm
getting a prefetch abort. If I try and load the data, I get a similar
error: "Unable to handle kernel paging request at virtual address 00003000"
where 0x3000 is the ROM address I'm trying to jump to / load from. How can
I pass execution to this address? Do I have to turn off the MMU? FYI, I'm
running a 2.2 variant on an XScale, and used inline assembly to generate the
load & the branch.

Thanks for your help,

-ian


2001-10-04 09:04:41

by Helge Hafting

[permalink] [raw]
Subject: Re: How can I jump to non-linux address space?

Ian Thompson wrote:
>
> Hi all,
>
> I'm sorry if this is off-topic, but I wasn't sure where else to ask...
>
> My kernel is running from RAM, and I want to jump to an address in ROM
> (which unfortunately, the kernel doesn't seem to know anything about). I

The kernel can get to know - all you need is code that maps the
ROM address range into some available virtual address range.
Look at device driver code - they do such mapping for ROM and/or
memory-based io regions.

> don't plan on trying to resume the kernel after doing this. However, I'm
> getting a prefetch abort. If I try and load the data, I get a similar
> error: "Unable to handle kernel paging request at virtual address 00003000"
> where 0x3000 is the ROM address I'm trying to jump to / load from. How can
> I pass execution to this address? Do I have to turn off the MMU?

How to set up the cpu before jumping to a ROM that won't return
can be tricky indeed. This depends on what that ROM code expect!

Do that ROM code work when the MMU has remapped its adresses so it
appears at some adress completely different from the bus address? (only
if it contains relative jumps only - no absolute addresses.) Does
it work with 4G segments? Does it work at all in protected mode,
with all interrupts routed to the linux kernel instead of the bios?
Does this code expect to find something (data, device interfaces,
vga memory) at certain addresses? If so, this must be mapped too.
For linux moves all this around.

In practise, existing ROM's tend to assume that the machine is
in a state close to what the bios initializes it to,
with 64k segments, no MMU, and a lot of assumptions about
how interrupts and hw devices are set up. _All_ of these
assumptions break after you start linux, and resetting
everything is so hard that it is usually done by
running the bios cold boot code.

Helge Hafting

2001-10-04 19:40:13

by Ian Thompson

[permalink] [raw]
Subject: RE: How can I jump to non-linux address space?

Helge,

Thanks for your advice! It's brought up a couple of other question, if you
don't mind:

> The kernel can get to know - all you need is code that maps the
> ROM address range into some available virtual address range.
> Look at device driver code - they do such mapping for ROM and/or
> memory-based io regions.

I've seen the mapping of the single RAM address range, but I don't see where
it is possible to add in another range for ROM. What functions should I
look for that do this mapping?

> Do that ROM code work when the MMU has remapped its adresses so it
> appears at some adress completely different from the bus address? (only
> if it contains relative jumps only - no absolute addresses.) Does
> it work with 4G segments? Does it work at all in protected mode,
> with all interrupts routed to the linux kernel instead of the bios?
> Does this code expect to find something (data, device interfaces,
> vga memory) at certain addresses? If so, this must be mapped too.

I've run this code (in ROM) successfully before starting the kernel. I
believe the cache is disabled, and interrupts are not needed (and are off).
The code does not refer to anything within the kernel. I've tried turning
off the MMU completely before branching, but this seems to hang the system.
=(

Any ideas of what I should look for to turn off, aside from just shutting
down the MMU? If I map the ROM address range into a virtual addr range,
won't I run into problems once I'm running the code, such as physical
addresses being interpreted by virtual ones?

btw, this is running on an XScale (strongARM).

Thanks again,
-ian

2001-10-04 20:33:00

by Richard B. Johnson

[permalink] [raw]
Subject: RE: How can I jump to non-linux address space?

On Thu, 4 Oct 2001, Ian Thompson wrote:

> Helge,
>
> Thanks for your advice! It's brought up a couple of other question, if you
> don't mind:
>
> > The kernel can get to know - all you need is code that maps the
> > ROM address range into some available virtual address range.
> > Look at device driver code - they do such mapping for ROM and/or
> > memory-based io regions.
>
> I've seen the mapping of the single RAM address range, but I don't see where
> it is possible to add in another range for ROM. What functions should I
> look for that do this mapping?
>
> > Do that ROM code work when the MMU has remapped its adresses so it
> > appears at some adress completely different from the bus address? (only
> > if it contains relative jumps only - no absolute addresses.) Does
> > it work with 4G segments? Does it work at all in protected mode,
> > with all interrupts routed to the linux kernel instead of the bios?
> > Does this code expect to find something (data, device interfaces,
> > vga memory) at certain addresses? If so, this must be mapped too.
>
> I've run this code (in ROM) successfully before starting the kernel. I
> believe the cache is disabled, and interrupts are not needed (and are off).
> The code does not refer to anything within the kernel. I've tried turning
> off the MMU completely before branching, but this seems to hang the system.
> =(
>
> Any ideas of what I should look for to turn off, aside from just shutting
> down the MMU? If I map the ROM address range into a virtual addr range,
> won't I run into problems once I'm running the code, such as physical
> addresses being interpreted by virtual ones?
>
> btw, this is running on an XScale (strongARM).
>
> Thanks again,
> -ian

All kernel addresses, including "physical" addresses are virtual.
There is a PTE to map your specific memory area, on a page-by-page
basis, to a virtual address. You are normally accessing memory
using that virtual address (both code and data).

If you want to turn OFF the virtual addressing, you have to first
jump or call a procedure, that is properly relocated, somewhere
that the virtual and physical addresses are the same. The first
one megabyte is such a location. It is complex and has to be done
in the correct order or else you can't "get back".

A typical way, on an ix86, is to make a binary array from the code
you want to execute, relocated from an object file, to some offset
such as 0x1000.

You use ioremap() to create a virtual address from 0x1000. Then
you copy the relocated code, currently in some array, to the relocated
address (0x1000), using the cookie returned from ioremap(). Then you
disable interrupts on your local CPU via a spinlock, you make a long
(FAR) call to your relocated code. In the code, you can disable the paging
bit and set DS, ES to the page-table selector, which looks at linear
addressing. Now you can see and access everything as 32-bit linear
address-space. Careful, you can't allow interrupts and you can't use the
stack because neither it, nor the stack-selector has been set. You can
do "your" thing, with global memory then you restore the previous
selectors to DS and ES, re-enable paging, then do a FAR return.
The code after the return unlocks the spin-lock and you are
home free.

I any step isn't done correctly, you will crash the processor ;^).
This shows up as though you hit the reset switch.

# insmod module.o <SPLAT> <FLASH>
Award Modular BIOS V45pg Copyright(c) 1998 Award Software, Inc.
Memory test : 0011240 Ok
Pres DEL to enter SETUP


Cheers,
Dick Johnson

Penguin : Linux version 2.4.1 on an i686 machine (799.53 BogoMips).

I was going to compile a list of innovations that could be
attributed to Microsoft. Once I realized that Ctrl-Alt-Del
was handled in the BIOS, I found that there aren't any.


2001-10-04 20:35:20

by Russell King

[permalink] [raw]
Subject: Re: How can I jump to non-linux address space?

On Wed, Oct 03, 2001 at 06:10:31PM -0700, Ian Thompson wrote:
> My kernel is running from RAM, and I want to jump to an address in ROM
> (which unfortunately, the kernel doesn't seem to know anything about).

Ok, its like you're rebooting, correct?

> I don't plan on trying to resume the kernel after doing this. However,
> I'm getting a prefetch abort.

That's because your address range for the ROM isn't mapped - when Linux
starts on ARM, it unmaps virtually everything, and remaps only the address
ranges it wants to use.

> How can I pass execution to this address? Do I have to turn off the MMU?

Essentially, you have 2 choices:

1. Turn off the MMU.
2. insert a 1:1 physical to virtual mapping for the ROM and call the code.
(with interrupts disabled).

Which one works depends on what the ROM code requires.

There is an example of (1) in the current ARM kernel sources - the RiscPC
port uses this method to reboot - we can't activate the hardware reset line
on these machines, so our only option is to use this method.

--
Russell King ([email protected]) The developer of ARM Linux
http://www.arm.linux.org.uk/personal/aboutme.html

2001-10-05 00:35:55

by Ian Thompson

[permalink] [raw]
Subject: RE: How can I jump to non-linux address space?

Hey Dick,

Thanks for the help! A couple more questions for you...

> You use ioremap() to create a virtual address from 0x1000. Then
> you copy the relocated code, currently in some array, to the relocated
> address (0x1000), using the cookie returned from ioremap().

How does this make the virtual address the same as the physical address? Or
are addr's in the first page (or 1st MB?) automatically mapped to the same
address when you call ioremap()? I printed out the __ioremap() addr's for
0x1000 and 0x3000, and neither of the virt addr's were equal to the physical
ones.

I tried something slightly different, which didn't work... Should it have?
What I did was put the code to turn off the MMU at physical address 0x3000,
and jumped to it (via branching to __ioremap(0x3000)). I think the branch
is working, since I can load the correct instruction via this reloc'ed
address. However, running the code that turns off the MMU instead reboots
the machine. =(

> In the code, you can disable the paging bit and set DS, ES to the
> page-table selector, which looks at linear addressing. Now you can
> see and access everything as 32-bit linear address-space.

Should I be looking for something else to twiddle instead of the MMU bit in
the CPU register? You mentioned the paging bit; is this different? Also,
what are DS & ES?

gracias,
-ian

2001-10-05 07:55:20

by Russell King

[permalink] [raw]
Subject: Re: How can I jump to non-linux address space?

On Thu, Oct 04, 2001 at 05:35:51PM -0700, Ian Thompson wrote:
> Hey Dick,
> Should I be looking for something else to twiddle instead of the MMU bit in
> the CPU register? You mentioned the paging bit; is this different? Also,
> what are DS & ES?

Dick is talking about x86 hardware, not your ARM hardware.

--
Russell King ([email protected]) The developer of ARM Linux
http://www.arm.linux.org.uk/personal/aboutme.html

2001-10-06 00:39:07

by Ian Thompson

[permalink] [raw]
Subject: RE: How can I jump to non-linux address space?

> Essentially, you have 2 choices:
>
> 1. Turn off the MMU.
> 2. insert a 1:1 physical to virtual mapping for the ROM and call the code.
> (with interrupts disabled).
>
> Which one works depends on what the ROM code requires.
>
> There is an example of (1) in the current ARM kernel sources - the RiscPC
> port uses this method to reboot - we can't activate the hardware reset
line
> on these machines, so our only option is to use this method.

I tried both of these, and I must be doing something wrong. For (1), I
grabbed the code you mentioned from the RiscPC port (setup_mm_for_reboot()
and some code from the soft reset routine). After calling
setup_mm_for_reboot, if I call __ioremap(), the processor hangs. If I shut
down the MMU, I get the same results.

Where would be a good place to find an example of how to implement your
second suggestion?

thanks. sorry to keep bothering you. i'll also try asking on the arm
newsgroup...
-ian

2001-10-06 07:57:37

by Russell King

[permalink] [raw]
Subject: Re: How can I jump to non-linux address space?

On Fri, Oct 05, 2001 at 05:38:53PM -0700, Ian Thompson wrote:
> I tried both of these, and I must be doing something wrong. For (1), I
> grabbed the code you mentioned from the RiscPC port (setup_mm_for_reboot()
> and some code from the soft reset routine). After calling
> setup_mm_for_reboot, if I call __ioremap(), the processor hangs. If I shut
> down the MMU, I get the same results.

You will need to disable interrupts if the machine vectors are located at
address 0 (check your boot logs with a recent kernel for a message like
"Vectors relocated to ...").

It's probably best to call cpu_proc_fin(), setup_mm_for_reboot() and
cpu_reset(address) directly rather than making your own copy - these
functions already do the right things for you. setup_mm_for_reboot()
will remap all of user space with a 1:1 virtual to physical mapping,
and hopefully on the Xscale, the two cpu_* functions do the intended
setup for this (I've not reviewed the xscale stuff in any great detail
yet though).

--
Russell King ([email protected]) The developer of ARM Linux
http://www.arm.linux.org.uk/personal/aboutme.html

2001-10-08 12:51:37

by Richard B. Johnson

[permalink] [raw]
Subject: RE: How can I jump to non-linux address space?

On Thu, 4 Oct 2001, Ian Thompson wrote:

> Hey Dick,
>
> Thanks for the help! A couple more questions for you...
>
> > You use ioremap() to create a virtual address from 0x1000. Then
> > you copy the relocated code, currently in some array, to the relocated
> > address (0x1000), using the cookie returned from ioremap().
>
> How does this make the virtual address the same as the physical address? Or
> are addr's in the first page (or 1st MB?) automatically mapped to the same
> address when you call ioremap()? I printed out the __ioremap() addr's for
> 0x1000 and 0x3000, and neither of the virt addr's were equal to the physical
> ones.
>

[Snipped...]
I was refering to Intel, not ARM hardware. You can look at the
setup code in your architecture specific tree and see if you
can figure it out. I have never even seen ARM hardware, much
less used it so I can't help there.

FYI, what you get from ioremap() is not a number that will mean
anything, even when it's mapped to the physical address. Often,
on some kernel versions, just to prevent module writers from
cheating, it is poisoned so it only works with the defined
macros. Therefore, it is a "cookie", not something you can
initialize a pointer with.

However, in Intel, for hacking only, the address that you can
use to initialize a pointer with is the address you re-mapped,
ORed with PAGE_OFFSET. If the address is below 1 megabyte,
there is a 1:1 mapping of virtual to physical addresses in
the Intel Linux kernel. This allows the page table to exist
at an address that can be accessed from 32-bit linear-mode
startup.

This is useful if you want to access something physical. For instance
if you want to find the physical address of bad memory. A user-mode
memory checker can exercise memory until something failed. Then it
can write some magic numbers on both sides of the failing address.

A kernel module could then search for the magic numbers, returning
the 32-bit linear offset of the bad memory. This is quicker than
writing a module to scan all the PTEs, returning the translation
offset.


Cheers,
Dick Johnson

Penguin : Linux version 2.4.1 on an i686 machine (799.53 BogoMips).

I was going to compile a list of innovations that could be
attributed to Microsoft. Once I realized that Ctrl-Alt-Del
was handled in the BIOS, I found that there aren't any.


2001-10-08 17:43:50

by Ian Thompson

[permalink] [raw]
Subject: RE: How can I jump to non-linux address space?

> functions already do the right things for you. setup_mm_for_reboot()
> will remap all of user space with a 1:1 virtual to physical mapping,

Am I correct in assuming that this will not remap the kernel address space?
If I'm trying to jump from the kernel to this physical address, will I need
to go through user space first?

-ian

2001-10-08 20:01:46

by Russell King

[permalink] [raw]
Subject: Re: How can I jump to non-linux address space?

On Mon, Oct 08, 2001 at 10:43:45AM -0700, Ian Thompson wrote:
> Am I correct in assuming that this will not remap the kernel address space?
> If I'm trying to jump from the kernel to this physical address, will I need
> to go through user space first?

If you want to execute code linked at address 0x3000, then you need to
execute it at address 0x3000.

Note that turning off the MMU will effectively change all the memory
mappings.

--
Russell King ([email protected]) The developer of ARM Linux
http://www.arm.linux.org.uk/personal/aboutme.html