Skip to contentSkip to conversation

ARM: Disable outer cache before kexec call

PATCH New Thomas Gleixner 6 participants 32 messages Jul 1, 2010 – Jul 5, 2010 1 message not in the archive

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover ARM: Disable outer cache before kexec call

The following patch series addresses the problem, that the kexec code does not disable the outer cache before disabling the inner cache and jumping into the new kernel. This results in random crashes of the new kernel.

Thanks,

tglx

A message that is not in the archive (sent off-list or not yet received): [email protected]

#4 lore raw

PATCH 2/2 arm: Implement l2x0 cache disable function

Quoted 18 lines

---------- Forwarded message ----------
From: Thomas Gleixner <[email protected]>
Date: Thu, Jul 1, 2010 at 9:35 PM
Subject: [patch 2/2] arm: Implement l2x0 cache disable function
To: LAK <[email protected]>
Cc: Catalin Marinas <[email protected]>

This function is called from kexec code before the inner caches are disabled to prevent random crashes in the new kernel.

Index: linux-2.6/arch/arm/mm/cache-l2x0.c
===================================================================

arch/arm/mm/cache-l2x0.c static void l2x0_flush_range(unsigned lo · line 213

Quoted 8 lines
arch/arm/mm/cache-l2x0.c +5 -0
@@ -206,6 +206,12 @@ static void l2x0_flush_range(unsigned lo
       spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
+static void l2x0_cache_disable(void)
+{
+       l2x0_inv_all();
+       writel(0, l2x0_base + L2X0_CTRL);
+}

I think it's a problem for PL310 controller where the access to L2X0_CTRL is secure, accessing this in non-secure mode will fault.

Srinidhi

#5 lore raw

PATCH 2/2 arm: Implement l2x0 cache disable function

In reply to Thomas Gleixner
Quoted 9 lines · Thomas Gleixner

This function is called from kexec code before the inner caches are disabled to prevent random crashes in the new kernel.

Index: linux-2.6/arch/arm/mm/cache-l2x0.c
===================================================================

arch/arm/mm/cache-l2x0.c static void l2x0_flush_range(unsigned lo · line 213

Quoted 8 lines
arch/arm/mm/cache-l2x0.c +5 -0
@@ -206,6 +206,12 @@ static void l2x0_flush_range(unsigned lo
        spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
+static void l2x0_cache_disable(void)
+{
+       l2x0_inv_all();
+       writel(0, l2x0_base + L2X0_CTRL);
+}

Even if we go this route, we need an l2x0_flush_all() rather than invalidate here as the latter removes dirty cache lines without evicting them first.

Signature · 1 line
Catalin
#6 lore raw

PATCH 2/2 arm: Implement l2x0 cache disable function

Quoted 13 lines · Catalin Marinas
Quoted 12 lines · Thomas Gleixner

This function is called from kexec code before the inner caches are disabled to prevent random crashes in the new kernel.

Index: linux-2.6/arch/arm/mm/cache-l2x0.c
===================================================================

arch/arm/mm/cache-l2x0.c +0 -0
@@ -206,6 +206,12 @@ static void l2x0_flush_range(unsigned lo
        spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
Quoted 9 lines

arch/arm/mm/cache-l2x0.c static void l2x0_flush_range(unsigned lo · line 213

Quoted 5 lines
arch/arm/mm/cache-l2x0.c +5 -0
+static void l2x0_cache_disable(void)
+{
+       l2x0_inv_all();
+       writel(0, l2x0_base + L2X0_CTRL);
+}

Even if we go this route, we need an l2x0_flush_all() rather than invalidate here as the latter removes dirty cache lines without evicting them first.

True, but that's an implementation detail. What's more important is to make a decision how to solve the problem as kexec is completely unusable for all L2 systems right now.

I think the correct way to deal with this is disabling L2 and let the OMAP3 folks deal with it. As Tony said there is some SMI magic to do that, so we can do the following:

In l2x0_init()

   if (!l2_enabled()) {
      if (non_secure()) {
      	 omap_smi_magic_l2_enable();
	 outer_cache.disable = omap_smi_magic_l2_disable;
      } else {
         sane_l2_enable();
	 outer_cache.disable = sane_l2_disable;
      }

}

Russells patch has its own merits nevertheless as a cleanup and simplification, but that's a separate issue.

Thoughts ?

tglx

#7 lore raw

PATCH 2/2 arm: Implement l2x0 cache disable function

Quoted 25 lines · Thomas Gleixner
Quoted 13 lines · Catalin Marinas
Quoted 12 lines · Thomas Gleixner

This function is called from kexec code before the inner caches are disabled to prevent random crashes in the new kernel.

Index: linux-2.6/arch/arm/mm/cache-l2x0.c
===================================================================

arch/arm/mm/cache-l2x0.c +0 -0
@@ -206,6 +206,12 @@ static void l2x0_flush_range(unsigned lo
        spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
Quoted 9 lines

arch/arm/mm/cache-l2x0.c static void l2x0_flush_range(unsigned lo · line 213

Quoted 5 lines
arch/arm/mm/cache-l2x0.c +5 -0
+static void l2x0_cache_disable(void)
+{
+       l2x0_inv_all();
+       writel(0, l2x0_base + L2X0_CTRL);
+}

Even if we go this route, we need an l2x0_flush_all() rather than invalidate here as the latter removes dirty cache lines without evicting them first.

Quoted 3 lines

True, but that's an implementation detail. What's more important is to make a decision how to solve the problem as kexec is completely unusable for all L2 systems right now.

My view is that we should try to find why cache flushing doesn't work but unfortunately I don't have any spare time to look into this (would need to use tools like ICE debugging/tracing).

Quoted 6 lines

I think the correct way to deal with this is disabling L2 and let the OMAP3 folks deal with it. As Tony said there is some SMI magic to do that, so we can do the following:

In l2x0_init()

Quoted 9 lines
   if (!l2_enabled()) {
      if (non_secure()) {
         omap_smi_magic_l2_enable();
         outer_cache.disable = omap_smi_magic_l2_disable;
      } else {
         sane_l2_enable();
         outer_cache.disable = sane_l2_disable;
      }
   }

The non_secure() bit is the key. AFAIK there isn't an easy way to check whether you are running in secure or non-secure mode (I think there is some CP14 debug register telling this but those registers aren't mandatory in a CPU implementation).

Signature · 1 line
Catalin
#8 lore raw

PATCH 2/2 arm: Implement l2x0 cache disable function

Quoted 26 lines · Catalin Marinas
Quoted 25 lines · Thomas Gleixner
Quoted 22 lines · Catalin Marinas
Quoted 9 lines · Thomas Gleixner

This function is called from kexec code before the inner caches are disabled to prevent random crashes in the new kernel.

Index: linux-2.6/arch/arm/mm/cache-l2x0.c
===================================================================

arch/arm/mm/cache-l2x0.c static void l2x0_flush_range(unsigned lo · line 213

Quoted 8 lines
arch/arm/mm/cache-l2x0.c +5 -0
@@ -206,6 +206,12 @@ static void l2x0_flush_range(unsigned lo
        spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
+static void l2x0_cache_disable(void)
+{
+       l2x0_inv_all();
+       writel(0, l2x0_base + L2X0_CTRL);
+}

Even if we go this route, we need an l2x0_flush_all() rather than invalidate here as the latter removes dirty cache lines without evicting them first.

Quoted 7 lines
Quoted 3 lines

True, but that's an implementation detail. What's more important is to make a decision how to solve the problem as kexec is completely unusable for all L2 systems right now.

My view is that we should try to find why cache flushing doesn't work but unfortunately I don't have any spare time to look into this (would need to use tools like ICE debugging/tracing).

Simply because you need to flush the cache in the decompressor of the new kernel before jumping into it and I doubt that Russell will be happy about adding an utter L2 mess to the decompressor.

Quoted 16 lines
Quoted 6 lines

I think the correct way to deal with this is disabling L2 and let the OMAP3 folks deal with it. As Tony said there is some SMI magic to do that, so we can do the following:

In l2x0_init()

Quoted 9 lines
   if (!l2_enabled()) {
      if (non_secure()) {
         omap_smi_magic_l2_enable();
         outer_cache.disable = omap_smi_magic_l2_disable;
      } else {
         sane_l2_enable();
         outer_cache.disable = sane_l2_disable;
      }
   }
Quoted 4 lines

The non_secure() bit is the key. AFAIK there isn't an easy way to check whether you are running in secure or non-secure mode (I think there is some CP14 debug register telling this but those registers aren't mandatory in a CPU implementation).

And how does the kernel decide whether it needs to use that SMI code in sleep34xx.S or not ?

Thanks,

tglx

#9 lore raw

PATCH 2/2 arm: Implement l2x0 cache disable function

Quoted 67 lines

-----Original Message-----
From: linux-arm-kernel-bounces at lists.infradead.org [mailto:linux-arm- kernel-bounces at lists.infradead.org] On Behalf Of Thomas Gleixner
Sent: Friday, July 02, 2010 10:10 PM
To: Catalin Marinas
Cc: Tony Lindgren; LAK
Subject: Re: [patch 2/2] arm: Implement l2x0 cache disable function

Quoted 5 lines · Catalin Marinas
Quoted 4 lines · Thomas Gleixner
Quoted 2 lines · Catalin Marinas
Quoted 1 line · Thomas Gleixner

This function is called from kexec code before the inner caches

are

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

disabled to prevent random crashes in the new kernel.

Index: linux-2.6/arch/arm/mm/cache-l2x0.c

===================================================================

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
arch/arm/mm/cache-l2x0.c +0 -0
@@ -206,6 +206,12 @@ static void l2x0_flush_range(unsigned lo
        spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

arch/arm/mm/cache-l2x0.c static void l2x0_flush_range(unsigned lo · line 213

Quoted 5 lines
arch/arm/mm/cache-l2x0.c +5 -0
+static void l2x0_cache_disable(void)
+{
+       l2x0_inv_all();
+       writel(0, l2x0_base + L2X0_CTRL);
+}

Even if we go this route, we need an l2x0_flush_all() rather than invalidate here as the latter removes dirty cache lines without

evicting

Quoted 9 lines
Quoted 5 lines
Quoted 1 line

them first.

True, but that's an implementation detail. What's more important is to make a decision how to solve the problem as kexec is completely unusable for all L2 systems right now.

My view is that we should try to find why cache flushing doesn't work but unfortunately I don't have any spare time to look into this (would need to use tools like ICE debugging/tracing).

Simply because you need to flush the cache in the decompressor of the new kernel before jumping into it and I doubt that Russell will be happy about adding an utter L2 mess to the decompressor.

Quoted 16 lines
Quoted 15 lines

I think the correct way to deal with this is disabling L2 and let the OMAP3 folks deal with it. As Tony said there is some SMI magic to do that, so we can do the following:

In l2x0_init()

   if (!l2_enabled()) {
      if (non_secure()) {
         omap_smi_magic_l2_enable();
         outer_cache.disable = omap_smi_magic_l2_disable;
      } else {
         sane_l2_enable();
         outer_cache.disable = sane_l2_disable;
      }
   }
Quoted 8 lines
Quoted 4 lines

The non_secure() bit is the key. AFAIK there isn't an easy way to check whether you are running in secure or non-secure mode (I think there is some CP14 debug register telling this but those registers aren't mandatory in a CPU implementation).

And how does the kernel decide whether it needs to use that SMI code in sleep34xx.S or not ?

It's OMAP3 specific code and SMI is the only way to enable or disable L2.

Regards,
Santosh

#10 lore raw

PATCH 2/2 arm: Implement l2x0 cache disable function

In reply to Thomas Gleixner
Quoted 32 lines · Thomas Gleixner
Quoted 30 lines · Catalin Marinas
Quoted 28 lines · Thomas Gleixner
Quoted 13 lines · Catalin Marinas
Quoted 12 lines · Thomas Gleixner

This function is called from kexec code before the inner caches are disabled to prevent random crashes in the new kernel.

Index: linux-2.6/arch/arm/mm/cache-l2x0.c
===================================================================

arch/arm/mm/cache-l2x0.c +0 -0
@@ -206,6 +206,12 @@ static void l2x0_flush_range(unsigned lo
        spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
Quoted 9 lines

arch/arm/mm/cache-l2x0.c static void l2x0_flush_range(unsigned lo · line 213

Quoted 5 lines
arch/arm/mm/cache-l2x0.c +5 -0
+static void l2x0_cache_disable(void)
+{
+       l2x0_inv_all();
+       writel(0, l2x0_base + L2X0_CTRL);
+}

Even if we go this route, we need an l2x0_flush_all() rather than invalidate here as the latter removes dirty cache lines without evicting them first.

True, but that's an implementation detail. What's more important is to make a decision how to solve the problem as kexec is completely unusable for all L2 systems right now.

Quoted 7 lines
Quoted 3 lines

My view is that we should try to find why cache flushing doesn't work but unfortunately I don't have any spare time to look into this (would need to use tools like ICE debugging/tracing).

Simply because you need to flush the cache in the decompressor of the new kernel before jumping into it and I doubt that Russell will be happy about adding an utter L2 mess to the decompressor.

The decompressor can create mappings as inner cacheable outer non-cacheable and it would not pollute the L2 cache.

I wonder how the OMAP guys deal with an already enabled L2 cache during decompressing.

Quoted 16 lines
Quoted 16 lines
Quoted 15 lines

I think the correct way to deal with this is disabling L2 and let the OMAP3 folks deal with it. As Tony said there is some SMI magic to do that, so we can do the following:

In l2x0_init()

   if (!l2_enabled()) {
      if (non_secure()) {
         omap_smi_magic_l2_enable();
         outer_cache.disable = omap_smi_magic_l2_disable;
      } else {
         sane_l2_enable();
         outer_cache.disable = sane_l2_disable;
      }
   }
Quoted 7 lines
Quoted 4 lines

The non_secure() bit is the key. AFAIK there isn't an easy way to check whether you are running in secure or non-secure mode (I think there is some CP14 debug register telling this but those registers aren't mandatory in a CPU implementation).

And how does the kernel decide whether it needs to use that SMI code in sleep34xx.S or not ?

I think that's done at configuration time. But ideally we shouldn't have different configurations for secure and non-secure modes.

Signature · 1 line
Catalin
#11 lore raw

PATCH 2/2 arm: Implement l2x0 cache disable function

Quoted 7 lines · Catalin Marinas
Quoted 3 lines · Thomas Gleixner

Simply because you need to flush the cache in the decompressor of the new kernel before jumping into it and I doubt that Russell will be happy about adding an utter L2 mess to the decompressor.

The decompressor can create mappings as inner cacheable outer non-cacheable and it would not pollute the L2 cache.

Doesn't solve the problem of boot loaders. Doesn't solve the problem that there may be dirty cache lines left in the L2 cache which could be searched.

There's too many "implementation" possibilities to bite us. The only safe thing to do is to disable the L2 cache.

#12 lore raw

PATCH 2/2 arm: Implement l2x0 cache disable function

In reply to Catalin Marinas
Quoted 53 lines

-----Original Message-----
From: linux-arm-kernel-bounces at lists.infradead.org [mailto:linux-arm- kernel-bounces at lists.infradead.org] On Behalf Of Catalin Marinas
Sent: Friday, July 02, 2010 10:25 PM
To: Thomas Gleixner
Cc: Tony Lindgren; LAK
Subject: Re: [patch 2/2] arm: Implement l2x0 cache disable function

Quoted 7 lines · Thomas Gleixner
Quoted 5 lines · Catalin Marinas
Quoted 4 lines · Thomas Gleixner
Quoted 2 lines · Catalin Marinas
Quoted 1 line · Thomas Gleixner

This function is called from kexec code before the inner caches

are

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

disabled to prevent random crashes in the new kernel.

Index: linux-2.6/arch/arm/mm/cache-l2x0.c

===================================================================

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
arch/arm/mm/cache-l2x0.c +0 -0
@@ -206,6 +206,12 @@ static void l2x0_flush_range(unsigned lo
        spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

arch/arm/mm/cache-l2x0.c static void l2x0_flush_range(unsigned lo · line 213

Quoted 5 lines
arch/arm/mm/cache-l2x0.c +5 -0
+static void l2x0_cache_disable(void)
+{
+       l2x0_inv_all();
+       writel(0, l2x0_base + L2X0_CTRL);
+}

Even if we go this route, we need an l2x0_flush_all() rather than invalidate here as the latter removes dirty cache lines without

evicting

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 1 line

them first.

True, but that's an implementation detail. What's more important is

to

Quoted 10 lines
Quoted 6 lines
Quoted 2 lines

make a decision how to solve the problem as kexec is completely unusable for all L2 systems right now.

My view is that we should try to find why cache flushing doesn't work but unfortunately I don't have any spare time to look into this (would need to use tools like ICE debugging/tracing).

Simply because you need to flush the cache in the decompressor of the new kernel before jumping into it and I doubt that Russell will be happy about adding an utter L2 mess to the decompressor.

Quoted 5 lines

The decompressor can create mappings as inner cacheable outer non-cacheable and it would not pollute the L2 cache.

I wonder how the OMAP guys deal with an already enabled L2 cache during decompressing.

$L2 isn't enabled during the decompression. It's done " __v7_setup" on OMAP3 and just before " l2x0_init" on OMAP4

#13 lore raw

PATCH 2/2 arm: Implement l2x0 cache disable function

Quoted 12 lines · Santosh
Quoted 12 lines

From: linux-arm-kernel-bounces at lists.infradead.org [mailto:linux-arm- kernel-bounces at lists.infradead.org] On Behalf Of Catalin Marinas

Quoted 8 lines · Thomas Gleixner
Quoted 3 lines · Catalin Marinas

My view is that we should try to find why cache flushing doesn't work but unfortunately I don't have any spare time to look into this (would need to use tools like ICE debugging/tracing).

Simply because you need to flush the cache in the decompressor of the new kernel before jumping into it and I doubt that Russell will be happy about adding an utter L2 mess to the decompressor.

Quoted 8 lines
Quoted 5 lines

The decompressor can create mappings as inner cacheable outer non-cacheable and it would not pollute the L2 cache.

I wonder how the OMAP guys deal with an already enabled L2 cache during decompressing.

$L2 isn't enabled during the decompression. It's done " __v7_setup" on OMAP3 and just before " l2x0_init" on OMAP4

Another approach would have been to mark the page tables as outer non-cacheable in the decompressor.

Anyway, if you already handle L2 cache initialisation during __v7_setup (and we could go back to a thread where I asked for per-platform CPU initialisation support), you could handle L2 cache disabling as well.

As per Thomas' patches, the outer_cache_fns structure has a .disable() function and this can be overridden by the OMAP code after l2x0_init().

The only additional comment I have to Thomas' second patch is that the l2x0_cache_disable() implementation has to flush (clean+inv) the L2 cache (rather than just invalidate) and then disable by writing the L2 control register. The L2 flushing function doesn't need to be exported via outer_cache_fns since it's only the disabling that would handle it.

Signature · 1 line
Catalin
#14 lore raw

PATCH 2/2 arm: Implement l2x0 cache disable function

Quoted 32 lines

-----Original Message-----
From: Catalin Marinas [mailto:catalin.marinas at arm.com]
Sent: Sunday, July 04, 2010 7:37 PM
To: Shilimkar, Santosh
Cc: Thomas Gleixner; Tony Lindgren; LAK
Subject: RE: [patch 2/2] arm: Implement l2x0 cache disable function

Quoted 5 lines · Santosh
Quoted 5 lines

From: linux-arm-kernel-bounces at lists.infradead.org [mailto:linux-arm- kernel-bounces at lists.infradead.org] On Behalf Of Catalin Marinas

Quoted 2 lines · Thomas Gleixner
Quoted 1 line · Catalin Marinas

My view is that we should try to find why cache flushing doesn't

work

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

but unfortunately I don't have any spare time to look into this

(would

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 1 line

need to use tools like ICE debugging/tracing).

Simply because you need to flush the cache in the decompressor of

the

Quoted 7 lines
Quoted 7 lines
Quoted 2 lines

new kernel before jumping into it and I doubt that Russell will be happy about adding an utter L2 mess to the decompressor.

The decompressor can create mappings as inner cacheable outer non-cacheable and it would not pollute the L2 cache.

I wonder how the OMAP guys deal with an already enabled L2 cache

during

Quoted 3 lines
Quoted 1 line

decompressing.

$L2 isn't enabled during the decompression. It's done " __v7_setup" on

OMAP3

Quoted 9 lines
Quoted 1 line

and just before " l2x0_init" on OMAP4

Another approach would have been to mark the page tables as outer non-cacheable in the decompressor.

Anyway, if you already handle L2 cache initialisation during __v7_setup (and we could go back to a thread where I asked for per-platform CPU initialisation support), you could handle L2 cache disabling as well.

Such a hook would be really nice. We have been discussing this for some time.

Quoted 3 lines

As per Thomas' patches, the outer_cache_fns structure has a .disable() function and this can be overridden by the OMAP code after l2x0_init().

Yes. That's good idea.

Quoted 6 lines

The only additional comment I have to Thomas' second patch is that the l2x0_cache_disable() implementation has to flush (clean+inv) the L2 cache (rather than just invalidate) and then disable by writing the L2 control register. The L2 flushing function doesn't need to be exported via outer_cache_fns since it's only the disabling that would handle it.

Exporting a l2x0_flush_all would be good as well if it's not a problem.

Regards,
Santosh

#15 lore raw

PATCH 2/2 arm: Implement l2x0 cache disable function

In reply to Shilimkar, Santosh
Quoted 13 lines · Santosh
Quoted 13 lines

-----Original Message-----
From: linux-arm-kernel-bounces at lists.infradead.org [mailto:linux-arm- kernel-bounces at lists.infradead.org] On Behalf Of Catalin Marinas
Sent: Friday, July 02, 2010 10:25 PM
To: Thomas Gleixner
Cc: Tony Lindgren; LAK
Subject: Re: [patch 2/2] arm: Implement l2x0 cache disable function

Quoted 3 lines · Thomas Gleixner

Simply because you need to flush the cache in the decompressor of the new kernel before jumping into it and I doubt that Russell will be happy about adding an utter L2 mess to the decompressor.

Quoted 8 lines
Quoted 5 lines

The decompressor can create mappings as inner cacheable outer non-cacheable and it would not pollute the L2 cache.

I wonder how the OMAP guys deal with an already enabled L2 cache during decompressing.

$L2 isn't enabled during the decompression. It's done " __v7_setup" on OMAP3 and just before " l2x0_init" on OMAP4

Not on the initial kernel, but on subsequent kernels booted via kexec, because of this issue, L2 will be enabled.

So when the decompressor comes to set up its own page tables - it uses the non-XP format - it ends up with tables setup with C=B=1, which is interpreted as fully cacheable. As the L2 unit is still enabled, this causes the region to be cacheable at L2 as well as L1 - which is not what the decompressor expects.

The result is that cache lines get loaded into L2, and marked dirty. When the decompressor then turns off the cache in order to call the kernel, things go awol because the dirty lines in L2 haven't been flushed out.

I think it's overkill to add L2 support to the decompressor - and I also think it's overkill to teach the decompressor about the XP format tables, especially as the booting documents clearly state that caches shall be disabled before calling the kernel. Also, we really can't insist that this is the case for boot loaders, and then do something different when we call the next kernel.

Also consider the case where the kernel you're booting into doesn't have L2 support enabled - maybe you disabled L2 support because it's causing problems. If you boot with L2 already enabled, then this kernel is going to struggle because it can't handle the enabled L2.

As I've already said, the boot loader case won't expect to be called via its reset vector with L2 enabled either - and could well go wrong if this is done.

So, all the ways you look at it, having L2 enabled when you're exiting the kernel to call some other piece of code (such as boot loader or decompressor) is asking for trouble.

Let's ensure that we bring L2 to a clean and disabled state prior to invoking a boot loader or subsequent kernel - even if that means we have to make SMC calls to achieve that.

#16 lore raw

PATCH 2/2 arm: Implement l2x0 cache disable function

Quoted 22 lines · Russell King - ARM Linux
Quoted 21 lines · Santosh
Quoted 13 lines

-----Original Message-----
From: linux-arm-kernel-bounces at lists.infradead.org [mailto:linux-arm- kernel-bounces at lists.infradead.org] On Behalf Of Catalin Marinas
Sent: Friday, July 02, 2010 10:25 PM
To: Thomas Gleixner
Cc: Tony Lindgren; LAK
Subject: Re: [patch 2/2] arm: Implement l2x0 cache disable function

Quoted 3 lines · Thomas Gleixner

Simply because you need to flush the cache in the decompressor of the new kernel before jumping into it and I doubt that Russell will be happy about adding an utter L2 mess to the decompressor.

Quoted 5 lines

The decompressor can create mappings as inner cacheable outer non-cacheable and it would not pollute the L2 cache.

I wonder how the OMAP guys deal with an already enabled L2 cache during decompressing.

$L2 isn't enabled during the decompression. It's done " __v7_setup" on OMAP3 and just before " l2x0_init" on OMAP4

[...]

Quoted 3 lines

I think it's overkill to add L2 support to the decompressor - and I also think it's overkill to teach the decompressor about the XP format tables,

Even if we did this, the secure software needs to be aware of OS rebooting since it may have a non-secure cacheable mapping which is speculatively fetched (and I was also told that the L2 treats the secure access bit as an address one - so you get separate cache lines for secure and non-secure).

Quoted 4 lines

Also consider the case where the kernel you're booting into doesn't have L2 support enabled - maybe you disabled L2 support because it's causing problems. If you boot with L2 already enabled, then this kernel is going to struggle because it can't handle the enabled L2.

You could have an outer non-cacheable cache policy and set the page table bits accordingly. But I agree with you, it's not worth the extra effort.

Quoted 3 lines

Let's ensure that we bring L2 to a clean and disabled state prior to invoking a boot loader or subsequent kernel - even if that means we have to make SMC calls to achieve that.

So Thomas' original patches are pretty much OK, with the exception of using clean+invalidate instead of just invalidate during disabling.

An additional question - do we need to flush the L2 cache again after it was disabled, just in case we got some speculative loads?

Signature · 1 line
Catalin
#17 lore raw

PATCH 2/2 arm: Implement l2x0 cache disable function

In reply to Catalin Marinas
Quoted 8 lines · Catalin Marinas
Quoted 3 lines · Thomas Gleixner

True, but that's an implementation detail. What's more important is to make a decision how to solve the problem as kexec is completely unusable for all L2 systems right now.

My view is that we should try to find why cache flushing doesn't work but unfortunately I don't have any spare time to look into this (would need to use tools like ICE debugging/tracing).

It's quite simple. If you leave the L2 cache enabled, then the decompressor needs to have L2 cache support in it, since it re-enables the cache. We really don't want to go down that route.

Moreover, boot loaders will not expect L2 cache to be enabled when we re-enter them for soft-reboot via their reset vector.

So, I agree with Thomas - we have to preserve the already stated requirements in the kernel booting document(s) which stipulate that caches must be disabled when the kernel is called.

#18 lore raw

PATCH 2/2 arm: Implement l2x0 cache disable function

In reply to Thomas Gleixner
Quoted 33 lines

-----Original Message-----
From: linux-arm-kernel-bounces at lists.infradead.org [mailto:linux-arm- kernel-bounces at lists.infradead.org] On Behalf Of Thomas Gleixner
Sent: Friday, July 02, 2010 9:00 PM
To: Catalin Marinas
Cc: Tony Lindgren; LAK
Subject: Re: [patch 2/2] arm: Implement l2x0 cache disable function

Quoted 13 lines · Catalin Marinas
Quoted 12 lines · Thomas Gleixner

This function is called from kexec code before the inner caches are disabled to prevent random crashes in the new kernel.

Index: linux-2.6/arch/arm/mm/cache-l2x0.c
===================================================================

arch/arm/mm/cache-l2x0.c +0 -0
@@ -206,6 +206,12 @@ static void l2x0_flush_range(unsigned lo
        spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
Quoted 9 lines

arch/arm/mm/cache-l2x0.c static void l2x0_flush_range(unsigned lo · line 213

Quoted 5 lines
arch/arm/mm/cache-l2x0.c +5 -0
+static void l2x0_cache_disable(void)
+{
+       l2x0_inv_all();
+       writel(0, l2x0_base + L2X0_CTRL);
+}

Even if we go this route, we need an l2x0_flush_all() rather than invalidate here as the latter removes dirty cache lines without evicting them first.

Quoted 8 lines

True, but that's an implementation detail. What's more important is to make a decision how to solve the problem as kexec is completely unusable for all L2 systems right now.

I think the correct way to deal with this is disabling L2 and let the OMAP3 folks deal with it. As Tony said there is some SMI magic to do that, so we can do the following:

Just to be clear OMAP3 don't have external(outer) L2 so the below point is actually applicable to OMAP4. If " omap_smi_magic_l2_enable" such a generic "secure_l2x_enable" hook which
can be populated by the archs should take care of OMAP problem

Quoted 23 lines

In l2x0_init()

   if (!l2_enabled()) {
      if (non_secure()) {
      	 omap_smi_magic_l2_enable();
	 outer_cache.disable = omap_smi_magic_l2_disable;
      } else {
         sane_l2_enable();
	 outer_cache.disable = sane_l2_disable;
      }
   }

Russells patch has its own merits nevertheless as a cleanup and simplification, but that's a separate issue.

Thoughts ?

tglx

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

#19 lore raw

cover ARM: Disable outer cache before kexec call

In reply to Thomas Gleixner

Hi Thomas,

Quoted 4 lines · Thomas Gleixner

The following patch series addresses the problem, that the kexec code does not disable the outer cache before disabling the inner cache and jumping into the new kernel. This results in random crashes of the new kernel.

We may need other ways to work around this problem. There are platforms like OMAP3 (I think) where the L2 cache cannot be disabled as Linux is running in non-secure (normal) mode.

Cannot some extra cache flushing work around this problem?

Signature · 1 line
Catalin
#20 lore raw

cover ARM: Disable outer cache before kexec call

Catalin,

Quoted 10 lines · Catalin Marinas
Quoted 4 lines · Thomas Gleixner

The following patch series addresses the problem, that the kexec code does not disable the outer cache before disabling the inner cache and jumping into the new kernel. This results in random crashes of the new kernel.

We may need other ways to work around this problem. There are platforms like OMAP3 (I think) where the L2 cache cannot be disabled as Linux is running in non-secure (normal) mode.

But it can disable the inner caches? That's weird.

Quoted 1 line

Cannot some extra cache flushing work around this problem?

We tried to keep the L2 on and just doing the l2x0_inv_all() call and it hangs.

Thanks,

tglx

#21 lore raw

cover ARM: Disable outer cache before kexec call

Quoted 7 lines · Thomas Gleixner
Quoted 6 lines · Catalin Marinas
Quoted 4 lines · Thomas Gleixner

The following patch series addresses the problem, that the kexec code does not disable the outer cache before disabling the inner cache and jumping into the new kernel. This results in random crashes of the new kernel.

Quoted 5 lines
Quoted 3 lines

We may need other ways to work around this problem. There are platforms like OMAP3 (I think) where the L2 cache cannot be disabled as Linux is running in non-secure (normal) mode.

But it can disable the inner caches? That's weird.

That's because the CP15 SCTLR register is a banked one, so independent configuration for secure and normal worlds.

The outer cache controller doesn't have banked registers and I'm not even sure it has a notion of secure or non-secure cache line.

Quoted 4 lines
Quoted 1 line

Cannot some extra cache flushing work around this problem?

We tried to keep the L2 on and just doing the l2x0_inv_all() call and it hangs.

You would probably need an l2x0_flush_all() function which does clean +invalidate. If you only do invalidate you may just remove valid cache lines that have reached the RAM.

We only use the l2x0_inv_all() prior to enabling the L2 cache.

Signature · 1 line
Catalin
#22 lore raw

cover ARM: Disable outer cache before kexec call

Catalin,

Quoted 12 lines · Catalin Marinas
Quoted 11 lines · Thomas Gleixner
Quoted 9 lines · Catalin Marinas
Quoted 4 lines · Thomas Gleixner

The following patch series addresses the problem, that the kexec code does not disable the outer cache before disabling the inner cache and jumping into the new kernel. This results in random crashes of the new kernel.

We may need other ways to work around this problem. There are platforms like OMAP3 (I think) where the L2 cache cannot be disabled as Linux is running in non-secure (normal) mode.

Quoted 7 lines
Quoted 1 line

But it can disable the inner caches? That's weird.

That's because the CP15 SCTLR register is a banked one, so independent configuration for secure and normal worlds.

The outer cache controller doesn't have banked registers and I'm not even sure it has a notion of secure or non-secure cache line.

Brilliant design.

Quoted 8 lines
Quoted 4 lines
Quoted 1 line

Cannot some extra cache flushing work around this problem?

We tried to keep the L2 on and just doing the l2x0_inv_all() call and it hangs.

You would probably need an l2x0_flush_all() function which does clean +invalidate. If you only do invalidate you may just remove valid cache lines that have reached the RAM.

Tried that, no fun.

Thanks,

tglx

#23 lore raw

cover ARM: Disable outer cache before kexec call

Quoted 16 lines · Thomas Gleixner
Quoted 14 lines · Catalin Marinas
Quoted 12 lines · Thomas Gleixner
Quoted 9 lines · Catalin Marinas
Quoted 4 lines · Thomas Gleixner

The following patch series addresses the problem, that the kexec code does not disable the outer cache before disabling the inner cache and jumping into the new kernel. This results in random crashes of the new kernel.

We may need other ways to work around this problem. There are platforms like OMAP3 (I think) where the L2 cache cannot be disabled as Linux is running in non-secure (normal) mode.

But it can disable the inner caches? That's weird.

Quoted 7 lines
Quoted 5 lines

That's because the CP15 SCTLR register is a banked one, so independent configuration for secure and normal worlds.

The outer cache controller doesn't have banked registers and I'm not even sure it has a notion of secure or non-secure cache line.

Brilliant design.

The L2 cache is in many time some device outside the CPU. But in this case I think it's more of a secure software issue - it could simply mark all the secure page tables as outer non-cacheable and just let the normal world handle the L2 cache as it likes.

Quoted 10 lines
Quoted 8 lines
Quoted 4 lines
Quoted 1 line

Cannot some extra cache flushing work around this problem?

We tried to keep the L2 on and just doing the l2x0_inv_all() call and it hangs.

You would probably need an l2x0_flush_all() function which does clean +invalidate. If you only do invalidate you may just remove valid cache lines that have reached the RAM.

Tried that, no fun.

Possibly some ordering issues. Please see Russell's reply on the cache flushing steps (I haven't tried them though).

Signature · 1 line
Catalin
#24 lore raw

cover ARM: Disable outer cache before kexec call

In reply to Thomas Gleixner
Quoted 18 lines · Thomas Gleixner

Catalin,

Quoted 14 lines · Catalin Marinas
Quoted 12 lines · Thomas Gleixner
Quoted 9 lines · Catalin Marinas
Quoted 4 lines · Thomas Gleixner

The following patch series addresses the problem, that the kexec code does not disable the outer cache before disabling the inner cache and jumping into the new kernel. This results in random crashes of the new kernel.

We may need other ways to work around this problem. There are platforms like OMAP3 (I think) where the L2 cache cannot be disabled as Linux is running in non-secure (normal) mode.

But it can disable the inner caches? That's weird.

Quoted 7 lines
Quoted 5 lines

That's because the CP15 SCTLR register is a banked one, so independent configuration for secure and normal worlds.

The outer cache controller doesn't have banked registers and I'm not even sure it has a notion of secure or non-secure cache line.

Brilliant design.

FYI, on secure omaps disabling/enabling/configuring of L2 cache must be done via SMI that runs some function in the secure software (PPA).. The code is there in arch/arm/mach-omap2/sleep34xx.S, just search for SMI or PPA.

Trying to set things via the ARM aux ctrl register will hang the system, so we need to figure out some way to select the L2 enable/disable based on if we're running in secure mode or not.

Regards,

Tony

#25 lore raw

cover ARM: Disable outer cache before kexec call

In reply to Thomas Gleixner
Quoted 18 lines

-----Original Message-----
From: linux-arm-kernel-bounces at lists.infradead.org [mailto:linux-arm- kernel-bounces at lists.infradead.org] On Behalf Of Thomas Gleixner
Sent: Thursday, July 01, 2010 9:59 PM
To: Catalin Marinas
Cc: LAK
Subject: Re: [patch 0/2] ARM: Disable outer cache before kexec call

Catalin,

Quoted 7 lines · Catalin Marinas
Quoted 4 lines · Thomas Gleixner

The following patch series addresses the problem, that the kexec code does not disable the outer cache before disabling the inner cache and jumping into the new kernel. This results in random crashes of the new kernel.

Quoted 6 lines
Quoted 3 lines

We may need other ways to work around this problem. There are platforms like OMAP3 (I think) where the L2 cache cannot be disabled as Linux is running in non-secure (normal) mode.

But it can disable the inner caches? That's weird.

If the C bit is disabled then it is as good as L1 and L2 are disabled.

Quoted 5 lines
Quoted 1 line

Cannot some extra cache flushing work around this problem?

We tried to keep the L2 on and just doing the l2x0_inv_all() call and it hangs.

Regards,
Santosh

#26 lore raw

cover ARM: Disable outer cache before kexec call

Quoted 18 lines · Santosh
Quoted 18 lines

-----Original Message-----
From: linux-arm-kernel-bounces at lists.infradead.org [mailto:linux-arm- kernel-bounces at lists.infradead.org] On Behalf Of Thomas Gleixner
Sent: Thursday, July 01, 2010 9:59 PM
To: Catalin Marinas
Cc: LAK
Subject: Re: [patch 0/2] ARM: Disable outer cache before kexec call

Catalin,

Quoted 7 lines · Catalin Marinas
Quoted 4 lines · Thomas Gleixner

The following patch series addresses the problem, that the kexec code does not disable the outer cache before disabling the inner cache and jumping into the new kernel. This results in random crashes of the new kernel.

Quoted 7 lines
Quoted 5 lines
Quoted 3 lines

We may need other ways to work around this problem. There are platforms like OMAP3 (I think) where the L2 cache cannot be disabled as Linux is running in non-secure (normal) mode.

But it can disable the inner caches? That's weird.

If the C bit is disabled then it is as good as L1 and L2 are disabled.

You may want to disable the I bit in SCTLR as well. With MMU disabled I think the D side acts as strongly ordered (uncached) but you can still get cached fetches in the I-cache (needs some TRM checking though). If that's the case, the L2 cache could see I-cache L1 fetches as cacheable accesses since L2 is a unified cache.

Signature · 1 line
Catalin
#27 lore raw

cover ARM: Disable outer cache before kexec call

Quoted 37 lines

-----Original Message-----
From: Catalin Marinas [mailto:catalin.marinas at arm.com]
Sent: Thursday, July 01, 2010 10:11 PM
To: Shilimkar, Santosh
Cc: Thomas Gleixner; LAK
Subject: RE: [patch 0/2] ARM: Disable outer cache before kexec call

Quoted 8 lines · Santosh
Quoted 8 lines

-----Original Message-----
From: linux-arm-kernel-bounces at lists.infradead.org [mailto:linux-arm- kernel-bounces at lists.infradead.org] On Behalf Of Thomas Gleixner
Sent: Thursday, July 01, 2010 9:59 PM
To: Catalin Marinas
Cc: LAK
Subject: Re: [patch 0/2] ARM: Disable outer cache before kexec call

Quoted 6 lines
Quoted 6 lines

Catalin,

Quoted 3 lines · Catalin Marinas
Quoted 1 line · Thomas Gleixner

The following patch series addresses the problem, that the kexec

code

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

does not disable the outer cache before disabling the inner cache

and

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

jumping into the new kernel. This results in random crashes of the

new

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 1 line

kernel.

We may need other ways to work around this problem. There are

platforms

Quoted 1 line
Quoted 1 line
Quoted 1 line

like OMAP3 (I think) where the L2 cache cannot be disabled as Linux

is

Quoted 4 lines
Quoted 3 lines
Quoted 1 line

running in non-secure (normal) mode.

But it can disable the inner caches? That's weird.

Quoted 8 lines
Quoted 1 line

If the C bit is disabled then it is as good as L1 and L2 are disabled.

You may want to disable the I bit in SCTLR as well. With MMU disabled I think the D side acts as strongly ordered (uncached) but you can still get cached fetches in the I-cache (needs some TRM checking though). If that's the case, the L2 cache could see I-cache L1 fetches as cacheable accesses since L2 is a unified cache.

You are right.

Regards,
Santosh

#28 lore raw

cover ARM: Disable outer cache before kexec call

In reply to Shilimkar, Santosh
Quoted 11 lines · Santosh
Quoted 10 lines

-----Original Message-----
From: linux-arm-kernel-bounces at lists.infradead.org [mailto:linux-arm- kernel-bounces at lists.infradead.org] On Behalf Of Thomas Gleixner
To: Catalin Marinas
Cc: LAK

Catalin,

But it can disable the inner caches? That's weird.

If the C bit is disabled then it is as good as L1 and L2 are disabled.

However, if L2 contains valid cache lines which haven't been written back, the result of disabling the C bit effectively is instantaneous memory corruption.

We know that some L2 caches aren't searched if the C bit in the MMU tables is disabled, and for those caches I'd imagine the same thing happens when you clear the C bit in the SCTLR when turning off the MMU. What we currently do for L1 is:

- disable interrupts
- clean all caches (optionally invalidating)
- turn off I & C bits
- invalidate all caches

The 'clean' is there to push dirty data out of the caches back into memory. We then turn off the I & C bits, ensuring that no new cache lines are read in. At this point, some caches are no longer searched.

We then invalidate all caches to ensure that when _something_ later re-enables the caches, that they don't see our stale data.

As L2 needs this same handling, what I propose is, for both machine_restart() and machine_kexec():

1. we move the IRQ (and FIQ) disable out of each CPUs proc_fin()
2. call flush_cache_all() immediately after IRQs are disabled (removing any cache flushing from proc_fin() implementations.)
3. call outer_flush_all() (new function) after that
4. call proc_fin() to disable the C & I bits.
5. call flush_cache_all() again to invalidate the inner caches
6. call outer_flush_all() again to invalidate the outer caches
7. whatever's next after the existing cpu_proc_fin()...

The only potential problem I see with this is that (5) and (6) may end up writing back some dirty data associated with the stack, and one of these functions may overwrite its return address - thereby causing us to loop back to (2) or (3). I don't think that's a problem as the next time around this 'loop' we won't be creating new dirty cache lines, and we should get to step 7.

#29 lore raw

cover ARM: Disable outer cache before kexec call

Quoted 46 lines · Russell King - ARM Linux
Quoted 11 lines · Santosh
Quoted 10 lines

-----Original Message-----
From: linux-arm-kernel-bounces at lists.infradead.org [mailto:linux-arm- kernel-bounces at lists.infradead.org] On Behalf Of Thomas Gleixner
To: Catalin Marinas
Cc: LAK

Catalin,

But it can disable the inner caches? That's weird.

If the C bit is disabled then it is as good as L1 and L2 are disabled.

However, if L2 contains valid cache lines which haven't been written back, the result of disabling the C bit effectively is instantaneous memory corruption.

We know that some L2 caches aren't searched if the C bit in the MMU tables is disabled, and for those caches I'd imagine the same thing happens when you clear the C bit in the SCTLR when turning off the MMU. What we currently do for L1 is:

- disable interrupts
- clean all caches (optionally invalidating)
- turn off I & C bits
- invalidate all caches

The 'clean' is there to push dirty data out of the caches back into memory. We then turn off the I & C bits, ensuring that no new cache lines are read in. At this point, some caches are no longer searched.

We then invalidate all caches to ensure that when _something_ later re-enables the caches, that they don't see our stale data.

As L2 needs this same handling, what I propose is, for both machine_restart() and machine_kexec():

1. we move the IRQ (and FIQ) disable out of each CPUs proc_fin()
2. call flush_cache_all() immediately after IRQs are disabled (removing any cache flushing from proc_fin() implementations.)
3. call outer_flush_all() (new function) after that
4. call proc_fin() to disable the C & I bits.
5. call flush_cache_all() again to invalidate the inner caches
6. call outer_flush_all() again to invalidate the outer caches
7. whatever's next after the existing cpu_proc_fin()...

Quoted 6 lines

The only potential problem I see with this is that (5) and (6) may end up writing back some dirty data associated with the stack, and one of these functions may overwrite its return address - thereby causing us to loop back to (2) or (3). I don't think that's a problem as the next time around this 'loop' we won't be creating new dirty cache lines, and we should get to step 7.

Right, something like this. I've not made an effort to fix the L2 bits, but I have marked the places where attention is required. The interesting bit is the first two files.

The next question this provokes is whether we need a dsb() between steps 3 and 4, and maybe again between 4 and 5, and possibly after 6 ?

 arch/arm/kernel/machine_kexec.c |    8 +++++++-
 arch/arm/kernel/process.c       |   20 ++++++++++++++++----
 arch/arm/mm/proc-arm1020.S      |    6 +-----
 arch/arm/mm/proc-arm1020e.S     |    6 +-----
 arch/arm/mm/proc-arm1022.S      |    6 +-----
 arch/arm/mm/proc-arm1026.S      |    6 +-----
 arch/arm/mm/proc-arm6_7.S       |    2 --
 arch/arm/mm/proc-arm720.S       |    6 +-----
 arch/arm/mm/proc-arm740.S       |    6 +-----
 arch/arm/mm/proc-arm7tdmi.S     |    2 --
 arch/arm/mm/proc-arm920.S       |   10 +---------
 arch/arm/mm/proc-arm922.S       |   10 +---------
 arch/arm/mm/proc-arm925.S       |    6 +-----
 arch/arm/mm/proc-arm926.S       |    6 +-----
 arch/arm/mm/proc-arm940.S       |    6 +-----
 arch/arm/mm/proc-arm946.S       |    6 +-----
 arch/arm/mm/proc-arm9tdmi.S     |    2 --
 arch/arm/mm/proc-fa526.S        |    6 +-----
 arch/arm/mm/proc-feroceon.S     |    8 ++------
 arch/arm/mm/proc-mohawk.S       |    6 +-----
 arch/arm/mm/proc-sa110.S        |    8 ++------
 arch/arm/mm/proc-sa1100.S       |    6 +-----
 arch/arm/mm/proc-v6.S           |    5 +----
 arch/arm/mm/proc-v7.S           |    5 +----
 arch/arm/mm/proc-xsc3.S         |    6 +-----
 arch/arm/mm/proc-xscale.S       |    6 +-----
 26 files changed, 46 insertions(+), 124 deletions(-)
arch/arm/kernel/machine_kexec.c +7 -1
index 598ca61..53f5dc4 100644
@@ -74,7 +74,13 @@ void machine_kexec(struct kimage *image)
 			   (unsigned long) reboot_code_buffer + KEXEC_CONTROL_PAGE_SIZE);
 	printk(KERN_INFO "Bye!\n");
 
-	cpu_proc_fin();
+	local_irq_disable();
+	local_fiq_disable();
 	setup_mm_for_reboot(0); /* mode is not used, so just pass 0*/
+	flush_cache_all();
+	/* XXX: L2 cache flush */
+	cpu_proc_fin();
+	flush_cache_all();
+	/* XXX: L2 cache flush */
 	cpu_reset(reboot_code_buffer_phys);
 }
arch/arm/kernel/process.c +16 -4
index acf5e6f..3736887 100644
@@ -84,10 +84,9 @@ __setup("hlt", hlt_setup);
 
 void arm_machine_restart(char mode, const char *cmd)
 {
-	/*
-	 * Clean and disable cache, and turn off interrupts
-	 */
-	cpu_proc_fin();
+	/* Disable interrupts first */
+	local_irq_disable();
+	local_fiq_disable();
 
 	/*
 	 * Tell the mm system that we are going to reboot -
@@ -96,6 +95,19 @@ void arm_machine_restart(char mode, const char *cmd)
 	 */
 	setup_mm_for_reboot(mode);
 
+	/* Clean and invalidate caches */
+	flush_cache_all();
+
+	/* XXX: L2 cache flush */
+
+	/* Turn off caching */
+	cpu_proc_fin();
+
+	/* Push out any further dirty data, and ensure cache is empty */
+	flush_cache_all();
+
+	/* XXX: L2 cache flush */
+
 	/*
 	 * Now call the architecture specific reboot code.
 	 */
arch/arm/mm/proc-arm1020.S +1 -5
index 72507c6..203a4e9 100644
@@ -79,15 +79,11 @@ ENTRY(cpu_arm1020_proc_init)
  * cpu_arm1020_proc_fin()
  */
 ENTRY(cpu_arm1020_proc_fin)
-	stmfd	sp!, {lr}
-	mov	ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-	msr	cpsr_c, ip
-	bl	arm1020_flush_kern_cache_all
 	mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
 	bic	r0, r0, #0x1000 		@ ...i............
 	bic	r0, r0, #0x000e 		@ ............wca.
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-	ldmfd	sp!, {pc}
+	mov	pc, lr
 
 /*
  * cpu_arm1020_reset(loc)
arch/arm/mm/proc-arm1020e.S +1 -5
index d278298..1a511e7 100644
@@ -79,15 +79,11 @@ ENTRY(cpu_arm1020e_proc_init)
  * cpu_arm1020e_proc_fin()
  */
 ENTRY(cpu_arm1020e_proc_fin)
-	stmfd	sp!, {lr}
-	mov	ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-	msr	cpsr_c, ip
-	bl	arm1020e_flush_kern_cache_all
 	mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
 	bic	r0, r0, #0x1000 		@ ...i............
 	bic	r0, r0, #0x000e 		@ ............wca.
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-	ldmfd	sp!, {pc}
+	mov	pc, lr
 
 /*
  * cpu_arm1020e_reset(loc)
arch/arm/mm/proc-arm1022.S +1 -5
index ce13e4a..1ffa4eb 100644
@@ -68,15 +68,11 @@ ENTRY(cpu_arm1022_proc_init)
  * cpu_arm1022_proc_fin()
  */
 ENTRY(cpu_arm1022_proc_fin)
-	stmfd	sp!, {lr}
-	mov	ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-	msr	cpsr_c, ip
-	bl	arm1022_flush_kern_cache_all
 	mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
 	bic	r0, r0, #0x1000 		@ ...i............
 	bic	r0, r0, #0x000e 		@ ............wca.
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-	ldmfd	sp!, {pc}
+	mov	pc, lr
 
 /*
  * cpu_arm1022_reset(loc)
arch/arm/mm/proc-arm1026.S +1 -5
index 636672a..5697c34 100644
@@ -68,15 +68,11 @@ ENTRY(cpu_arm1026_proc_init)
  * cpu_arm1026_proc_fin()
  */
 ENTRY(cpu_arm1026_proc_fin)
-	stmfd	sp!, {lr}
-	mov	ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-	msr	cpsr_c, ip
-	bl	arm1026_flush_kern_cache_all
 	mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
 	bic	r0, r0, #0x1000 		@ ...i............
 	bic	r0, r0, #0x000e 		@ ............wca.
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-	ldmfd	sp!, {pc}
+	mov	pc, lr
 
 /*
  * cpu_arm1026_reset(loc)
arch/arm/mm/proc-arm6_7.S +0 -2
index 795dc61..64e0b32 100644
@@ -184,8 +184,6 @@ ENTRY(cpu_arm7_proc_init)
 
 ENTRY(cpu_arm6_proc_fin)
 ENTRY(cpu_arm7_proc_fin)
-		mov	r0, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-		msr	cpsr_c, r0
 		mov	r0, #0x31			@ ....S..DP...M
 		mcr	p15, 0, r0, c1, c0, 0		@ disable caches
 		mov	pc, lr
arch/arm/mm/proc-arm720.S +1 -5
index 0b62de2..9d96824 100644
@@ -54,15 +54,11 @@ ENTRY(cpu_arm720_proc_init)
 		mov	pc, lr
 
 ENTRY(cpu_arm720_proc_fin)
-		stmfd	sp!, {lr}
-		mov	ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-		msr	cpsr_c, ip
 		mrc	p15, 0, r0, c1, c0, 0
 		bic	r0, r0, #0x1000			@ ...i............
 		bic	r0, r0, #0x000e			@ ............wca.
 		mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-		mcr	p15, 0, r1, c7, c7, 0		@ invalidate cache
-		ldmfd	sp!, {pc}
+		mov	pc, lr
 
 /*
  * Function: arm720_proc_do_idle(void)
arch/arm/mm/proc-arm740.S +1 -5
index 01860cd..6c1a9ab 100644
@@ -36,15 +36,11 @@ ENTRY(cpu_arm740_switch_mm)
  * cpu_arm740_proc_fin()
  */
 ENTRY(cpu_arm740_proc_fin)
-	stmfd	sp!, {lr}
-	mov	ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-	msr	cpsr_c, ip
 	mrc	p15, 0, r0, c1, c0, 0
 	bic	r0, r0, #0x3f000000		@ bank/f/lock/s
 	bic	r0, r0, #0x0000000c		@ w-buffer/cache
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-	mcr	p15, 0, r0, c7, c0, 0		@ invalidate cache
-	ldmfd	sp!, {pc}
+	mov	pc, lr
 
 /*
  * cpu_arm740_reset(loc)
arch/arm/mm/proc-arm7tdmi.S +0 -2
index 1201b98..6a850db 100644
@@ -36,8 +36,6 @@ ENTRY(cpu_arm7tdmi_switch_mm)
  * cpu_arm7tdmi_proc_fin()
  */
 ENTRY(cpu_arm7tdmi_proc_fin)
-		mov	r0, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-		msr	cpsr_c, r0
 		mov	pc, lr
 
 /*
arch/arm/mm/proc-arm920.S +1 -9
index 8be8199..86f80aa 100644
@@ -69,19 +69,11 @@ ENTRY(cpu_arm920_proc_init)
  * cpu_arm920_proc_fin()
  */
 ENTRY(cpu_arm920_proc_fin)
-	stmfd	sp!, {lr}
-	mov	ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-	msr	cpsr_c, ip
-#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
-	bl	arm920_flush_kern_cache_all
-#else
-	bl	v4wt_flush_kern_cache_all
-#endif
 	mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
 	bic	r0, r0, #0x1000			@ ...i............
 	bic	r0, r0, #0x000e			@ ............wca.
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-	ldmfd	sp!, {pc}
+	mov	pc, lr
 
 /*
  * cpu_arm920_reset(loc)
arch/arm/mm/proc-arm922.S +1 -9
index c0ff8e4..f76ce9b 100644
@@ -71,19 +71,11 @@ ENTRY(cpu_arm922_proc_init)
  * cpu_arm922_proc_fin()
  */
 ENTRY(cpu_arm922_proc_fin)
-	stmfd	sp!, {lr}
-	mov	ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-	msr	cpsr_c, ip
-#ifndef CONFIG_CPU_DCACHE_WRITETHROUGH
-	bl	arm922_flush_kern_cache_all
-#else
-	bl	v4wt_flush_kern_cache_all
-#endif
 	mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
 	bic	r0, r0, #0x1000			@ ...i............
 	bic	r0, r0, #0x000e			@ ............wca.
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-	ldmfd	sp!, {pc}
+	mov	pc, lr
 
 /*
  * cpu_arm922_reset(loc)
arch/arm/mm/proc-arm925.S +1 -5
index 3c6cffe..657bd3f 100644
@@ -92,15 +92,11 @@ ENTRY(cpu_arm925_proc_init)
  * cpu_arm925_proc_fin()
  */
 ENTRY(cpu_arm925_proc_fin)
-	stmfd	sp!, {lr}
-	mov	ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-	msr	cpsr_c, ip
-	bl	arm925_flush_kern_cache_all
 	mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
 	bic	r0, r0, #0x1000			@ ...i............
 	bic	r0, r0, #0x000e			@ ............wca.
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-	ldmfd	sp!, {pc}
+	mov	pc, lr
 
 /*
  * cpu_arm925_reset(loc)
arch/arm/mm/proc-arm926.S +1 -5
index 75b707c..73f1f3c 100644
@@ -61,15 +61,11 @@ ENTRY(cpu_arm926_proc_init)
  * cpu_arm926_proc_fin()
  */
 ENTRY(cpu_arm926_proc_fin)
-	stmfd	sp!, {lr}
-	mov	ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-	msr	cpsr_c, ip
-	bl	arm926_flush_kern_cache_all
 	mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
 	bic	r0, r0, #0x1000			@ ...i............
 	bic	r0, r0, #0x000e			@ ............wca.
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-	ldmfd	sp!, {pc}
+	mov	pc, lr
 
 /*
  * cpu_arm926_reset(loc)
arch/arm/mm/proc-arm940.S +1 -5
index 1af1657..fffb061 100644
@@ -37,15 +37,11 @@ ENTRY(cpu_arm940_switch_mm)
  * cpu_arm940_proc_fin()
  */
 ENTRY(cpu_arm940_proc_fin)
-	stmfd	sp!, {lr}
-	mov	ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-	msr	cpsr_c, ip
-	bl	arm940_flush_kern_cache_all
 	mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
 	bic	r0, r0, #0x00001000		@ i-cache
 	bic	r0, r0, #0x00000004		@ d-cache
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-	ldmfd	sp!, {pc}
+	mov	pc, lr
 
 /*
  * cpu_arm940_reset(loc)
arch/arm/mm/proc-arm946.S +1 -5
index 1664b6a..249a605 100644
@@ -44,15 +44,11 @@ ENTRY(cpu_arm946_switch_mm)
  * cpu_arm946_proc_fin()
  */
 ENTRY(cpu_arm946_proc_fin)
-	stmfd	sp!, {lr}
-	mov	ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-	msr	cpsr_c, ip
-	bl	arm946_flush_kern_cache_all
 	mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
 	bic	r0, r0, #0x00001000		@ i-cache
 	bic	r0, r0, #0x00000004		@ d-cache
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-	ldmfd	sp!, {pc}
+	mov	pc, lr
 
 /*
  * cpu_arm946_reset(loc)
arch/arm/mm/proc-arm9tdmi.S +0 -2
index 28545c2..db47566 100644
@@ -36,8 +36,6 @@ ENTRY(cpu_arm9tdmi_switch_mm)
  * cpu_arm9tdmi_proc_fin()
  */
 ENTRY(cpu_arm9tdmi_proc_fin)
-		mov	r0, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-		msr	cpsr_c, r0
 		mov	pc, lr
 
 /*
arch/arm/mm/proc-fa526.S +1 -5
index 08f5ac2..7803fdf 100644
@@ -39,17 +39,13 @@ ENTRY(cpu_fa526_proc_init)
  * cpu_fa526_proc_fin()
  */
 ENTRY(cpu_fa526_proc_fin)
-	stmfd	sp!, {lr}
-	mov	ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-	msr	cpsr_c, ip
-	bl	fa_flush_kern_cache_all
 	mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
 	bic	r0, r0, #0x1000			@ ...i............
 	bic	r0, r0, #0x000e			@ ............wca.
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
 	nop
 	nop
-	ldmfd	sp!, {pc}
+	mov	pc, lr
 
 /*
  * cpu_fa526_reset(loc)
arch/arm/mm/proc-feroceon.S +2 -6
index 53e6323..f8eff28 100644
@@ -75,13 +75,9 @@ ENTRY(cpu_feroceon_proc_init)
  * cpu_feroceon_proc_fin()
  */
 ENTRY(cpu_feroceon_proc_fin)
-	stmfd	sp!, {lr}
-	mov	ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-	msr	cpsr_c, ip
-	bl	feroceon_flush_kern_cache_all
-
 #if defined(CONFIG_CACHE_FEROCEON_L2) && \
 	!defined(CONFIG_CACHE_FEROCEON_L2_WRITETHROUGH)
+	XXX: move me to L2 cache support
 	mov	r0, #0
 	mcr	p15, 1, r0, c15, c9, 0		@ clean L2
 	mcr	p15, 0, r0, c7, c10, 4		@ drain WB
@@ -91,7 +87,7 @@ ENTRY(cpu_feroceon_proc_fin)
 	bic	r0, r0, #0x1000			@ ...i............
 	bic	r0, r0, #0x000e			@ ............wca.
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-	ldmfd	sp!, {pc}
+	mov	pc, lr
 
 /*
  * cpu_feroceon_reset(loc)
arch/arm/mm/proc-mohawk.S +1 -5
index caa3115..5f6892f 100644
@@ -51,15 +51,11 @@ ENTRY(cpu_mohawk_proc_init)
  * cpu_mohawk_proc_fin()
  */
 ENTRY(cpu_mohawk_proc_fin)
-	stmfd	sp!, {lr}
-	mov	ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-	msr	cpsr_c, ip
-	bl	mohawk_flush_kern_cache_all
 	mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
 	bic	r0, r0, #0x1800			@ ...iz...........
 	bic	r0, r0, #0x0006			@ .............ca.
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-	ldmfd	sp!, {pc}
+	mov	pc, lr
 
 /*
  * cpu_mohawk_reset(loc)
arch/arm/mm/proc-sa110.S +2 -6
index 7b706b3..a201eb0 100644
@@ -44,17 +44,13 @@ ENTRY(cpu_sa110_proc_init)
  * cpu_sa110_proc_fin()
  */
 ENTRY(cpu_sa110_proc_fin)
-	stmfd	sp!, {lr}
-	mov	ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-	msr	cpsr_c, ip
-	bl	v4wb_flush_kern_cache_all	@ clean caches
-1:	mov	r0, #0
+	mov	r0, #0
 	mcr	p15, 0, r0, c15, c2, 2		@ Disable clock switching
 	mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
 	bic	r0, r0, #0x1000			@ ...i............
 	bic	r0, r0, #0x000e			@ ............wca.
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-	ldmfd	sp!, {pc}
+	mov	pc, lr
 
 /*
  * cpu_sa110_reset(loc)
arch/arm/mm/proc-sa1100.S +1 -5
index 5c47760..7ddc480 100644
@@ -55,16 +55,12 @@ ENTRY(cpu_sa1100_proc_init)
  *  - Clean and turn off caches.
  */
 ENTRY(cpu_sa1100_proc_fin)
-	stmfd	sp!, {lr}
-	mov	ip, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
-	msr	cpsr_c, ip
-	bl	v4wb_flush_kern_cache_all
 	mcr	p15, 0, ip, c15, c2, 2		@ Disable clock switching
 	mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
 	bic	r0, r0, #0x1000			@ ...i............
 	bic	r0, r0, #0x000e			@ ............wca.
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-	ldmfd	sp!, {pc}
+	mov	pc, lr
 
 /*
  * cpu_sa1100_reset(loc)
arch/arm/mm/proc-v6.S +1 -4
index 7a5337e..e55ffd7 100644
@@ -42,14 +42,11 @@ ENTRY(cpu_v6_proc_init)
 	mov	pc, lr
 
 ENTRY(cpu_v6_proc_fin)
-	stmfd	sp!, {lr}
-	cpsid	if				@ disable interrupts
-	bl	v6_flush_kern_cache_all
 	mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
 	bic	r0, r0, #0x1000			@ ...i............
 	bic	r0, r0, #0x0006			@ .............ca.
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-	ldmfd	sp!, {pc}
+	mov	pc, lr
 
 /*
  *	cpu_v6_reset(loc)
arch/arm/mm/proc-v7.S +1 -4
index 7aaf88a..b0f8873 100644
@@ -45,14 +45,11 @@ ENTRY(cpu_v7_proc_init)
 ENDPROC(cpu_v7_proc_init)
 
 ENTRY(cpu_v7_proc_fin)
-	stmfd	sp!, {lr}
-	cpsid	if				@ disable interrupts
-	bl	v7_flush_kern_cache_all
 	mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
 	bic	r0, r0, #0x1000			@ ...i............
 	bic	r0, r0, #0x0006			@ .............ca.
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-	ldmfd	sp!, {pc}
+	mov	pc, lr
 ENDPROC(cpu_v7_proc_fin)
 
 /*
arch/arm/mm/proc-xsc3.S +1 -5
index e5797f1..361a51e 100644
@@ -90,15 +90,11 @@ ENTRY(cpu_xsc3_proc_init)
  * cpu_xsc3_proc_fin()
  */
 ENTRY(cpu_xsc3_proc_fin)
-	str	lr, [sp, #-4]!
-	mov	r0, #PSR_F_BIT|PSR_I_BIT|SVC_MODE
-	msr	cpsr_c, r0
-	bl	xsc3_flush_kern_cache_all	@ clean caches
 	mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
 	bic	r0, r0, #0x1800			@ ...IZ...........
 	bic	r0, r0, #0x0006			@ .............CA.
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-	ldr	pc, [sp], #4
+	mov	pc, lr
 
 /*
  * cpu_xsc3_reset(loc)
arch/arm/mm/proc-xscale.S +1 -5
index 63037e2..1407597 100644
@@ -124,15 +124,11 @@ ENTRY(cpu_xscale_proc_init)
  * cpu_xscale_proc_fin()
  */
 ENTRY(cpu_xscale_proc_fin)
-	str	lr, [sp, #-4]!
-	mov	r0, #PSR_F_BIT|PSR_I_BIT|SVC_MODE
-	msr	cpsr_c, r0
-	bl	xscale_flush_kern_cache_all	@ clean caches
 	mrc	p15, 0, r0, c1, c0, 0		@ ctrl register
 	bic	r0, r0, #0x1800			@ ...IZ...........
 	bic	r0, r0, #0x0006			@ .............CA.
 	mcr	p15, 0, r0, c1, c0, 0		@ disable caches
-	ldr	pc, [sp], #4
+	mov	pc, lr
 
 /*
  * cpu_xscale_reset(loc)
#30 lore raw

cover ARM: Disable outer cache before kexec call

Quoted 54 lines · Russell King - ARM Linux
Quoted 46 lines · Russell King - ARM Linux
Quoted 11 lines · Santosh
Quoted 10 lines

-----Original Message-----
From: linux-arm-kernel-bounces at lists.infradead.org [mailto:linux-arm- kernel-bounces at lists.infradead.org] On Behalf Of Thomas Gleixner
To: Catalin Marinas
Cc: LAK

Catalin,

But it can disable the inner caches? That's weird.

If the C bit is disabled then it is as good as L1 and L2 are disabled.

However, if L2 contains valid cache lines which haven't been written back, the result of disabling the C bit effectively is instantaneous memory corruption.

We know that some L2 caches aren't searched if the C bit in the MMU tables is disabled, and for those caches I'd imagine the same thing happens when you clear the C bit in the SCTLR when turning off the MMU. What we currently do for L1 is:

- disable interrupts
- clean all caches (optionally invalidating)
- turn off I & C bits
- invalidate all caches

The 'clean' is there to push dirty data out of the caches back into memory. We then turn off the I & C bits, ensuring that no new cache lines are read in. At this point, some caches are no longer searched.

We then invalidate all caches to ensure that when _something_ later re-enables the caches, that they don't see our stale data.

As L2 needs this same handling, what I propose is, for both machine_restart() and machine_kexec():

1. we move the IRQ (and FIQ) disable out of each CPUs proc_fin()
2. call flush_cache_all() immediately after IRQs are disabled (removing any cache flushing from proc_fin() implementations.)
3. call outer_flush_all() (new function) after that
4. call proc_fin() to disable the C & I bits.
5. call flush_cache_all() again to invalidate the inner caches
6. call outer_flush_all() again to invalidate the outer caches
7. whatever's next after the existing cpu_proc_fin()...

Quoted 6 lines

The only potential problem I see with this is that (5) and (6) may end up writing back some dirty data associated with the stack, and one of these functions may overwrite its return address - thereby causing us to loop back to (2) or (3). I don't think that's a problem as the next time around this 'loop' we won't be creating new dirty cache lines, and we should get to step 7.

Quoted 6 lines

Right, something like this. I've not made an effort to fix the L2 bits, but I have marked the places where attention is required. The interesting bit is the first two files.

The next question this provokes is whether we need a dsb() between steps 3 and 4, and maybe again between 4 and 5, and possibly after 6 ?

We don't need a dsb() after 3 and 6. The outer_flush_all() function would need to do a cache_sync (which polls a register) since that's a background operation on the L2 cache. DSB doesn't have any effect on the L2 cache.

As for between 4 and 5, we may need an ISB as well (not sure about DSB). It's worth checking the recommended MMU disabling sequences in the ARM ARM (if any).

Signature · 1 line
Catalin
#31 lore raw

cover ARM: Disable outer cache before kexec call

Quoted 22 lines · Catalin Marinas
Quoted 21 lines · Russell King - ARM Linux
Quoted 9 lines · Russell King - ARM Linux

1. we move the IRQ (and FIQ) disable out of each CPUs proc_fin()
2. call flush_cache_all() immediately after IRQs are disabled (removing any cache flushing from proc_fin() implementations.)
3. call outer_flush_all() (new function) after that
4. call proc_fin() to disable the C & I bits.
5. call flush_cache_all() again to invalidate the inner caches
6. call outer_flush_all() again to invalidate the outer caches
7. whatever's next after the existing cpu_proc_fin()...

Quoted 6 lines

The only potential problem I see with this is that (5) and (6) may end up writing back some dirty data associated with the stack, and one of these functions may overwrite its return address - thereby causing us to loop back to (2) or (3). I don't think that's a problem as the next time around this 'loop' we won't be creating new dirty cache lines, and we should get to step 7.

Right, something like this. I've not made an effort to fix the L2 bits, but I have marked the places where attention is required. The interesting bit is the first two files.

Quoted 7 lines
Quoted 2 lines

The next question this provokes is whether we need a dsb() between steps 3 and 4, and maybe again between 4 and 5, and possibly after 6 ?

We don't need a dsb() after 3 and 6. The outer_flush_all() function would need to do a cache_sync (which polls a register) since that's a background operation on the L2 cache. DSB doesn't have any effect on the L2 cache.

Well, that leaves a question about Feroceon:

#if defined(CONFIG_CACHE_FEROCEON_L2) && \
        !defined(CONFIG_CACHE_FEROCEON_L2_WRITETHROUGH)
        mov     r0, #0
        mcr     p15, 1, r0, c15, c9, 0          @ clean L2
        mcr     p15, 0, r0, c7, c10, 4          @ drain WB

#endif

The question is where to move that (effective) DSB to - I guess it can move to arch/arm/mm/cache-feroceon-l2.c.

#32 lore raw

cover ARM: Disable outer cache before kexec call

In reply to Russell King - ARM Linux
Quoted 6 lines · Russell King - ARM Linux
Quoted 1 line · Russell King - ARM Linux

On Thu, Jul 01, 2010 at 10:08:37PM +0530, Shilimkar, Santosh wrote:

Right, something like this. I've not made an effort to fix the L2 bits, but I have marked the places where attention is required. The interesting bit is the first two files.

I filled in the L2 bits for l2x0 in the following way:

arch/arm/mm/cache-l2x0.c +13 -0
Index: linux-2.6/arch/arm/mm/cache-l2x0.c
===================================================================
@@ -103,6 +103,18 @@ static void l2x0_cache_sync(void)
 	spin_unlock_irqrestore(&l2x0_lock, flags);
 }
 
+static inline void l2x0_flush_all(void)
+{
+	unsigned long flags;
+
+	/* invalidate all ways */
+	spin_lock_irqsave(&l2x0_lock, flags);
+	writel(l2x0_way_mask, l2x0_base + L2X0_CLEAN_INV_WAY);
+	cache_wait(l2x0_base + L2X0_CLEAN_INV_WAY, l2x0_way_mask);
+	cache_sync();
+	spin_unlock_irqrestore(&l2x0_lock, flags);
+}
+
 static inline void l2x0_inv_all(void)
 {
 	unsigned long flags;
@@ -262,6 +274,7 @@ void __init l2x0_init(void __iomem *base
 	outer_cache.clean_range = l2x0_clean_range;
 	outer_cache.flush_range = l2x0_flush_range;
 	outer_cache.sync = l2x0_cache_sync;
+	outer_cache.flush_all = l2x0_cache_disable;
 
 	printk(KERN_INFO "%s cache controller enabled\n", type);
 	printk(KERN_INFO "l2x0: %d ways, CACHE_ID 0x%08x, AUX_CTRL 0x%08x\n",

Now I added outer_flush_all() calls to the places Russell pointed out.

Unfortunately that works just 5 times in a row, then the box stops dead _after_ the decompressor jumps into the kernel. With current mainline we fail in the decompressor itself. I guess we need the same magic in the decompressor as well.

Adding the L2 disable function on top of these patches works like a charm.

Seriously I think that keeping L2 enabled has enough unknown side effects which might be dealt with by some random cache flushes, but that's not a real solution to the underlying problem.

As a matter of sanity I think we should add the L2 disable functionality. It's logical and it _DOES_ work. Further it solves a real world problem as kexec is broken on any L2 enabled system AFAICT.

The sillyness of OMAP3 can be dealt with by those who invented that insanity w/o imposing their wreckage on everyone.

Thanks,

tglx