diff mbox

[U-Boot,1/4] x86: Implement reset_cpu() correctly for modern CPUs

Message ID 1429974275-13743-1-git-send-email-sjg@chromium.org
State Superseded
Delegated to: Simon Glass
Headers show

Commit Message

Simon Glass April 25, 2015, 3:04 p.m. UTC
The existing code is pretty ancient and is unreliable on modern hardware.
Generally it will hang.

We can use port 0xcf9 to initiate reset on more modern hardware (say in the
last 10 years). Update the reset_cpu() function to do this, and add a new
'full reset' function to perform a full power cycle.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

 arch/x86/cpu/cpu.c               | 22 +++++++++-------------
 arch/x86/include/asm/processor.h | 11 +++++++++++
 2 files changed, 20 insertions(+), 13 deletions(-)

Comments

Bin Meng April 27, 2015, 4:56 a.m. UTC | #1
Hi Simon,

On Sat, Apr 25, 2015 at 11:04 PM, Simon Glass <sjg@chromium.org> wrote:
> The existing code is pretty ancient and is unreliable on modern hardware.
> Generally it will hang.
>
> We can use port 0xcf9 to initiate reset on more modern hardware (say in the
> last 10 years). Update the reset_cpu() function to do this, and add a new
> 'full reset' function to perform a full power cycle.
>
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
>
>  arch/x86/cpu/cpu.c               | 22 +++++++++-------------
>  arch/x86/include/asm/processor.h | 11 +++++++++++
>  2 files changed, 20 insertions(+), 13 deletions(-)
>
> diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c
> index c9614f1..13b3baa 100644
> --- a/arch/x86/cpu/cpu.c
> +++ b/arch/x86/cpu/cpu.c
> @@ -380,21 +380,17 @@ void  flush_cache(unsigned long dummy1, unsigned long dummy2)
>         asm("wbinvd\n");
>  }
>
> -void __attribute__ ((regparm(0))) generate_gpf(void);
> -
> -/* segment 0x70 is an arbitrary segment which does not exist */
> -asm(".globl generate_gpf\n"
> -       ".hidden generate_gpf\n"
> -       ".type generate_gpf, @function\n"
> -       "generate_gpf:\n"
> -       "ljmp   $0x70, $0x47114711\n");
> -
>  __weak void reset_cpu(ulong addr)
>  {
> -       printf("Resetting using x86 Triple Fault\n");
> -       set_vector(13, generate_gpf);   /* general protection fault handler */
> -       set_vector(8, generate_gpf);    /* double fault handler */
> -       generate_gpf();                 /* start the show */
> +       /* Do a hard reset through the chipset's reset control register */
> +       outb(SYS_RST | RST_CPU, PORT_RESET);
> +       for (;;)
> +               cpu_hlt();
> +}
> +
> +void x86_full_reset(void)
> +{
> +       outb(FULL_RST, PORT_RESET);
>  }
>
>  int dcache_status(void)
> diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
> index 3e26202..a24028d 100644
> --- a/arch/x86/include/asm/processor.h
> +++ b/arch/x86/include/asm/processor.h
> @@ -27,6 +27,17 @@
>
>  #define PORT_RESET             0xcf9
>
> +enum {
> +       SYS_RST         = 1 << 1,       /* clear for soft reset, set for hard */

The comment looks confusing. What does 'clear for soft reset' mean?

> +       RST_CPU         = 1 << 2,       /* initiate reset */

CPU_RST? to follow the same convention as SYS_RST and FULL_RST?

> +       FULL_RST        = 1 << 3,       /* full power cycle */
> +};
> +
> +/**
> + * x86_full_reset() - reset everything: perform a full power cycle
> + */
> +void x86_full_reset(void);
> +
>  static inline __attribute__((always_inline)) void cpu_hlt(void)
>  {
>         asm("hlt");
> --

Regards,
Bin
Simon Glass April 29, 2015, 2:08 a.m. UTC | #2
Hi Bin,

On 26 April 2015 at 22:56, Bin Meng <bmeng.cn@gmail.com> wrote:
> Hi Simon,
>
> On Sat, Apr 25, 2015 at 11:04 PM, Simon Glass <sjg@chromium.org> wrote:
>> The existing code is pretty ancient and is unreliable on modern hardware.
>> Generally it will hang.
>>
>> We can use port 0xcf9 to initiate reset on more modern hardware (say in the
>> last 10 years). Update the reset_cpu() function to do this, and add a new
>> 'full reset' function to perform a full power cycle.
>>
>> Signed-off-by: Simon Glass <sjg@chromium.org>
>> ---
>>
>>  arch/x86/cpu/cpu.c               | 22 +++++++++-------------
>>  arch/x86/include/asm/processor.h | 11 +++++++++++
>>  2 files changed, 20 insertions(+), 13 deletions(-)
>>
>> diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c
>> index c9614f1..13b3baa 100644
>> --- a/arch/x86/cpu/cpu.c
>> +++ b/arch/x86/cpu/cpu.c
>> @@ -380,21 +380,17 @@ void  flush_cache(unsigned long dummy1, unsigned long dummy2)
>>         asm("wbinvd\n");
>>  }
>>
>> -void __attribute__ ((regparm(0))) generate_gpf(void);
>> -
>> -/* segment 0x70 is an arbitrary segment which does not exist */
>> -asm(".globl generate_gpf\n"
>> -       ".hidden generate_gpf\n"
>> -       ".type generate_gpf, @function\n"
>> -       "generate_gpf:\n"
>> -       "ljmp   $0x70, $0x47114711\n");
>> -
>>  __weak void reset_cpu(ulong addr)
>>  {
>> -       printf("Resetting using x86 Triple Fault\n");
>> -       set_vector(13, generate_gpf);   /* general protection fault handler */
>> -       set_vector(8, generate_gpf);    /* double fault handler */
>> -       generate_gpf();                 /* start the show */
>> +       /* Do a hard reset through the chipset's reset control register */
>> +       outb(SYS_RST | RST_CPU, PORT_RESET);
>> +       for (;;)
>> +               cpu_hlt();
>> +}
>> +
>> +void x86_full_reset(void)
>> +{
>> +       outb(FULL_RST, PORT_RESET);
>>  }
>>
>>  int dcache_status(void)
>> diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
>> index 3e26202..a24028d 100644
>> --- a/arch/x86/include/asm/processor.h
>> +++ b/arch/x86/include/asm/processor.h
>> @@ -27,6 +27,17 @@
>>
>>  #define PORT_RESET             0xcf9
>>
>> +enum {
>> +       SYS_RST         = 1 << 1,       /* clear for soft reset, set for hard */
>
> The comment looks confusing. What does 'clear for soft reset' mean?
>
>> +       RST_CPU         = 1 << 2,       /* initiate reset */
>
> CPU_RST? to follow the same convention as SYS_RST and FULL_RST?

It should do, but I want to follow Intel's register naming. I'll add a
bit more detail of where this came from.

>
>> +       FULL_RST        = 1 << 3,       /* full power cycle */
>> +};
>> +
>> +/**
>> + * x86_full_reset() - reset everything: perform a full power cycle
>> + */
>> +void x86_full_reset(void);
>> +
>>  static inline __attribute__((always_inline)) void cpu_hlt(void)
>>  {
>>         asm("hlt");
>> --
>

Regards,
Simon
diff mbox

Patch

diff --git a/arch/x86/cpu/cpu.c b/arch/x86/cpu/cpu.c
index c9614f1..13b3baa 100644
--- a/arch/x86/cpu/cpu.c
+++ b/arch/x86/cpu/cpu.c
@@ -380,21 +380,17 @@  void  flush_cache(unsigned long dummy1, unsigned long dummy2)
 	asm("wbinvd\n");
 }
 
-void __attribute__ ((regparm(0))) generate_gpf(void);
-
-/* segment 0x70 is an arbitrary segment which does not exist */
-asm(".globl generate_gpf\n"
-	".hidden generate_gpf\n"
-	".type generate_gpf, @function\n"
-	"generate_gpf:\n"
-	"ljmp   $0x70, $0x47114711\n");
-
 __weak void reset_cpu(ulong addr)
 {
-	printf("Resetting using x86 Triple Fault\n");
-	set_vector(13, generate_gpf);	/* general protection fault handler */
-	set_vector(8, generate_gpf);	/* double fault handler */
-	generate_gpf();			/* start the show */
+	/* Do a hard reset through the chipset's reset control register */
+	outb(SYS_RST | RST_CPU, PORT_RESET);
+	for (;;)
+		cpu_hlt();
+}
+
+void x86_full_reset(void)
+{
+	outb(FULL_RST, PORT_RESET);
 }
 
 int dcache_status(void)
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index 3e26202..a24028d 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -27,6 +27,17 @@ 
 
 #define PORT_RESET		0xcf9
 
+enum {
+	SYS_RST		= 1 << 1,	/* clear for soft reset, set for hard */
+	RST_CPU		= 1 << 2,	/* initiate reset */
+	FULL_RST	= 1 << 3,	/* full power cycle */
+};
+
+/**
+ * x86_full_reset() - reset everything: perform a full power cycle
+ */
+void x86_full_reset(void);
+
 static inline __attribute__((always_inline)) void cpu_hlt(void)
 {
 	asm("hlt");