On Fri, Aug 10, 2007 at 06:57:15PM +0200, Nicolas Schichan wrote:
> I now use __flush_cache_all(), since flush_cache_all() is mostly a noop on
> if cpu_has_dc_aliases evaluates to 0 (and it is the case on the MIPS cpu I
> have access to). To avoid caching problem in the relocation code, I disable
> the cache in KSEG0 (using the K0 field in CP0 CONFIG register) before jumping
> to the relocation code. I don't know how clean this solution is, but this
> avoids having to add non-portable cache flush code to relocate_kernel.S.
> Signed-off-by: Nicolas Schichan <email@example.com>
> --- linux/arch/mips/kernel/machine_kexec.c (revision 5939)
> +++ linux/arch/mips/kernel/machine_kexec.c (revision 5941)
> @@ -50,8 +50,10 @@
> reboot_code_buffer =
> (unsigned long)page_address(image->control_code_page);
> + printk(KERN_INFO "reboot code is at %08lx\n", reboot_code_buffer);
> kexec_start_address = image->start;
> - kexec_indirection_page = phys_to_virt(image->head & PAGE_MASK);
> + kexec_indirection_page = (long)phys_to_virt(image->head & PAGE_MASK);
> memcpy((void*)reboot_code_buffer, relocate_new_kernel,
> @@ -75,11 +77,17 @@
> - flush_icache_range(reboot_code_buffer,
> - reboot_code_buffer + KEXEC_CONTROL_CODE_SIZE);
> + __flush_cache_all();
> + flush_icache_all();
flush_icache_all() is a nop except on VIVT caches. Anyway, following a
__flush_cache_all it's pointless unless you want to flush caches a little
> - printk("Will call new kernel at %08x\n", image->start);
> - printk("Bye ...\n");
> - flush_cache_all();
> + /*
> + * avoid cache operation related headache in
> + * relocate_kernel.S: disable caches in kseg0, the new kernel
> + * will take care to re-enable cache in kseg0.
> + */
> + change_c0_config(CONF_CM_CMASK, CONF_CM_UNCACHED);
And that line is the kernel's one way ticket to hell on some platforms.
There is a hazard barrier missing here.
Only KSEG0 (or CKSEG0 in 64-bit parlance) is affected by changing Config.K0
field. But 64-bit kernels do not necessarily run in one of the 32-bit
compatibility segments (R8000 doesn't but that's an academic counter
example) - if they exist at all there is no guarantee that there is any RAM
mapped in them (IP30 and others).
On IP27 the kernel will run in CKSEG0 and if you switch that to 0 it'll mean
BRIDGE ASIC will start looking at the uncached attribute which will be
defaulted to 0 meaning that in CKSEG0 the CPU will no longer address RAM but
the ECC and Backdoor Directory information.
Basically it boils down to be very, very careful about cache modes
on MIPS. What the kernel does on bootup only happens to work because for
all the platforms that have potencial issues with the change of the CCA
for KSEG0 we know that Linux will set the CCA to the same value that is
already in that field. So that mode switch is sort of useless for most
platforms except a few where firmware doesn't initialize Config0.K0.