Thomas Letan
2015-Aug-04 06:55 UTC
[syslinux] [PATCH] efi: leaving long mode in kernel_jump routine
Syslinux 6.03 (efi64) fails to boot a 32-bit kernel. The way Syslinux leaves long mode in kernel_jump assembly routine does not follow AMD64 specifications. More precisely: 1. After setting a new GADT, `cs` has to be refresh by doing a long jump, but it is not 2. Other segments have to be updated, but they are not 3. Disabling paging has to be done before disabling long mode, but the implementation does the opposite In most cases, a computer that tries to execute the kernel_jump routine reboot (it can also hangs). This patch fixes the kernel_jump routine. Signed-off-by: Thomas Letan <thomas.letan at ssi.gouv.fr> --- efi/x86_64/linux.S | 61 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/efi/x86_64/linux.S b/efi/x86_64/linux.S index 0a0e996..972c0b2 100644 --- a/efi/x86_64/linux.S +++ b/efi/x86_64/linux.S @@ -10,8 +10,9 @@ * * ----------------------------------------------------------------------- */ -#define CR0_PG_FLAG 0x80000000 -#define MSR_EFER 0xc0000080 +#define CR0_PG_BIT 31 +#define CR4_PAE_BIT 5 +#define MSR_EFER 0xc0000080 .globl kernel_jump .type kernel_jump, at function @@ -19,30 +20,50 @@ kernel_jump: cli - /* - * Setup our segment selector (0x10) and return address (%rdi) - * on the stack in preparation for the far return below. - */ - mov $0x1000000000, %rcx - addq %rcx, %rdi - pushq %rdi + /* save the content of rsi (boot_param argument of kernel_jump function) */ + mov %rsi, %rbx + + call base_address +base_address: + pop %rsi + + /* need to perform a long jump to update cs + + /* load absolute address of pm_code in jmp_address location */ + lea (pm_code - base_address)(%rsi, 1), %rax + mov %eax, (jmp_address - base_address)(%rsi, 1) + + ljmp *(jmp_address - base_address)(%rsi, 1) + +jmp_address: + .long 0 /* address */ + .word 0x10 /* segment */ .code32 pm_code: - /* Disable IA-32e mode by clearing IA32_EFER.LME */ - xorl %eax, %eax - xorl %edx, %edx - movl $MSR_EFER, %ecx - wrmsr + /* cs segment has been updated, now update the rest */ + mov $0x18, %eax + mov %eax, %ds + mov %eax, %es + mov %eax, %fs + mov %eax, %gs + mov %eax, %ss - /* Turn off paging to disable long mode */ - movl %cr0, %eax - andl $~CR0_PG_FLAG, %eax - movl %eax, %cr0 + /* disable paging. */ + mov %cr0, %eax + btr $CR0_PG_BIT, %eax /* PG in CR0 */ + mov %eax, %cr0 + + /* disable long mode. */ + mov $MSR_EFER, %ecx + rdmsr + btr $8, %eax + wrmsr - /* Far return */ - lret + /* kernel jump */ + mov %ebx, %esi + jmp *%edi .code64 .align 4 -- 2.5.0
Patrick Masotta
2015-Aug-04 07:58 UTC
[syslinux] [PATCH] efi: leaving long mode in kernel_jump routine
>>>Syslinux 6.03 (efi64) fails to boot a 32-bit kernel. The way Syslinux leaves long mode in kernel_jump assembly routine does not follow AMD64 specifications. More precisely: 1. After setting a new GADT, `cs` has to be refresh by doing a long jump, but it is not 2. Other segments have to be updated, but they are not 3. Disabling paging has to be done before disabling long mode, but the implementation does the opposite In most cases, a computer that tries to execute the kernel_jump routine reboot (it can also hangs). This patch fixes the kernel_jump routine. Signed-off-by: Thomas Letan <thomas.letan at ssi.gouv.fr> <<< Have you tested it? Have you checked that efi64 loading 64bit kernels is OK from a kernel_jump point of view? Thanks for the patch. Best, Patrick
intrigeri
2015-Aug-04 09:13 UTC
[syslinux] [PATCH] efi: leaving long mode in kernel_jump routine
Hi, Thomas Letan via Syslinux wrote (04 Aug 2015 06:55:48 GMT) :> Syslinux 6.03 (efi64) fails to boot a 32-bit kernel.Maybe I'm missing something, but this has been working flawlessly for us in Tails so far. What exactly fails? Cheers, -- intrigeri
Thomas Letan
2015-Aug-04 09:27 UTC
[syslinux] [PATCH] efi: leaving long mode in kernel_jump routine
Hi> Maybe I'm missing something, but this has been working flawlessly for > us in Tails so far. What exactly fails?Are you using EFI Handover Protocol? It might explain the difference Thomas
Thomas Letan
2015-Aug-04 09:34 UTC
[syslinux] [PATCH] efi: leaving long mode in kernel_jump routine
Also it works on QEMU. Le 04/08/2015 11:13, intrigeri via Syslinux a ?crit :> Hi, > > Thomas Letan via Syslinux wrote (04 Aug 2015 06:55:48 GMT) : >> Syslinux 6.03 (efi64) fails to boot a 32-bit kernel. > > Maybe I'm missing something, but this has been working flawlessly for > us in Tails so far. What exactly fails? > > Cheers, > -- > intrigeri > _______________________________________________ > Syslinux mailing list > Submissions to Syslinux at zytor.com > Unsubscribe or set options at: > http://www.zytor.com/mailman/listinfo/syslinux >
Celelibi
2015-Aug-04 17:31 UTC
[syslinux] [PATCH] efi: leaving long mode in kernel_jump routine
2015-08-04 11:13 UTC+02:00, intrigeri via Syslinux <syslinux at zytor.com>:> Hi, > > Thomas Letan via Syslinux wrote (04 Aug 2015 06:55:48 GMT) : >> Syslinux 6.03 (efi64) fails to boot a 32-bit kernel. > > Maybe I'm missing something, but this has been working flawlessly for > us in Tails so far. What exactly fails? > > Cheers, > -- > intrigeriSome time ago I've had a booting issue I traced to exactly the code that is fixed by this patch. The effect was simply that the (physical) machine rebooted exactly on the wrmsr instruction. The result of my investigation was that the code wasn't following the protocol (by intel and by AMD) for leaving long mode as pointed out by Thomas Letan. I unfortunately left this in standby for too long. I think qemu and some processors are just more permissive and allow the current code to work. The CPU I tried to boot on is the AMD A4-5300 APU. Given the code involved, I'm not sure it is relevant whether we want to boot a 32 bits or 64 bits kernel. My 2 cents. Celelibi
Ady
2015-Aug-08 11:28 UTC
[syslinux] [PATCH] efi: leaving long mode in kernel_jump routine
> Syslinux 6.03 (efi64) fails to boot a 32-bit kernel. The way Syslinux > leaves long mode in kernel_jump assembly routine does not follow AMD64 > specifications. More precisely: > 1. After setting a new GADT, `cs` has to be refresh by doing a long > jump, but it is not > 2. Other segments have to be updated, but they are not > 3. Disabling paging has to be done before disabling long mode, but the > implementation does the opposite > > In most cases, a computer that tries to execute the kernel_jump routine > reboot (it can also hangs). This patch fixes the kernel_jump routine. >Is this patch going to be yet another one that will be "lost" in the mailing list? Is this patch incorrect? Not useful? Unnecessary? Newer kernels are supposed to respect certain rules / standards / protocols, and IF everything is done "adequately", then Syslinux (BIOS / EFI*) should be able to boot such newish kernels. What about older ones? Those that are not %100 compliant with newer rules / protocols / standards? TIA, Ady.
Patrick Masotta
2015-Aug-08 17:04 UTC
[syslinux] [PATCH] efi: leaving long mode in kernel_jump routine
>>>> Syslinux 6.03 (efi64) fails to boot a 32-bit kernel. The way Syslinux > leaves long mode in kernel_jump assembly routine does not follow AMD64 > specifications. More precisely: > 1. After setting a new GADT, `cs` has to be refresh by doing a long > jump, but it is not > 2. Other segments have to be updated, but they are not > 3. Disabling paging has to be done before disabling long mode, but the > implementation does the opposite > > In most cases, computer that tries to execute the kernel_jump routine > reboot (it can also hangs). This patch fixes the kernel_jump routine. > Is this patch going to be yet another one that will be "lost" in the mailing list? Is this patch incorrect? Not useful? Unnecessary? <<< It seems "it is" necessary, I'll be probably testing it next week.>>>Newer kernels are supposed to respect certain rules / standards / protocols, and IF everything is done "adequately", then Syslinux (BIOS / EFI*) should be able to boot such newish kernels. <<< AFAIK it seems this patch is even necessary for new kernels in the case EFI64 boots a 32 Bit kernel, right? Best, Patrick
Gene Cumm
2015-Aug-23 18:09 UTC
[syslinux] [PATCH] efi: leaving long mode in kernel_jump routine
On Tue, Aug 4, 2015 at 2:55 AM, Thomas Letan via Syslinux <syslinux at zytor.com> wrote:> Syslinux 6.03 (efi64) fails to boot a 32-bit kernel. The way Syslinux > leaves long mode in kernel_jump assembly routine does not follow AMD64 > specifications. More precisely: > 1. After setting a new GADT, `cs` has to be refresh by doing a long > jump, but it is not > 2. Other segments have to be updated, but they are not > 3. Disabling paging has to be done before disabling long mode, but the > implementation does the opposite > > In most cases, a computer that tries to execute the kernel_jump routine > reboot (it can also hangs). This patch fixes the kernel_jump routine. > > Signed-off-by: Thomas Letan <thomas.letan at ssi.gouv.fr>Thomas, I noticed you created bug ID 62 ( http://bugzilla.syslinux.org/show_bug.cgi?id=62 ) which appears to be a duplicate of bug 38 ( http://bugzilla.syslinux.org/show_bug.cgi?id=38 ). Is there any reason this isn't a duplicate? -- -Gene
Gene Cumm
2015-Aug-23 18:57 UTC
[syslinux] [PATCH] efi: leaving long mode in kernel_jump routine
On Tue, Aug 4, 2015 at 2:55 AM, Thomas Letan via Syslinux <syslinux at zytor.com> wrote:> Syslinux 6.03 (efi64) fails to boot a 32-bit kernel. The way Syslinux > leaves long mode in kernel_jump assembly routine does not follow AMD64 > specifications. More precisely: > 1. After setting a new GADT, `cs` has to be refresh by doing a long > jump, but it is not > 2. Other segments have to be updated, but they are not > 3. Disabling paging has to be done before disabling long mode, but the > implementation does the opposite > > In most cases, a computer that tries to execute the kernel_jump routine > reboot (it can also hangs). This patch fixes the kernel_jump routine. > > Signed-off-by: Thomas Letan <thomas.letan at ssi.gouv.fr> > --- > efi/x86_64/linux.S | 61 > ++++++++++++++++++++++++++++++++++++------------------ > 1 file changed, 41 insertions(+), 20 deletions(-) >Your mailer or something else mangled this patch pretty well as the copy in my mailbox and on the archives are unusable. This however is. diff --git a/efi/x86_64/linux.S b/efi/x86_64/linux.S index 0a0e996..972c0b2 100644 --- a/efi/x86_64/linux.S +++ b/efi/x86_64/linux.S @@ -10,8 +10,9 @@ * * ----------------------------------------------------------------------- */ -#define CR0_PG_FLAG 0x80000000 -#define MSR_EFER 0xc0000080 +#define CR0_PG_BIT 31 +#define CR4_PAE_BIT 5 +#define MSR_EFER 0xc0000080 .globl kernel_jump .type kernel_jump, at function @@ -19,30 +20,50 @@ kernel_jump: cli - /* - * Setup our segment selector (0x10) and return address (%rdi) - * on the stack in preparation for the far return below. - */ - mov $0x1000000000, %rcx - addq %rcx, %rdi - pushq %rdi + /* save the content of rsi (boot_param argument of kernel_jump function) */ + mov %rsi, %rbx + + call base_address +base_address: + pop %rsi + + /* need to perform a long jump to update cs + + /* load absolute address of pm_code in jmp_address location */ + lea (pm_code - base_address)(%rsi, 1), %rax + mov %eax, (jmp_address - base_address)(%rsi, 1) + + ljmp *(jmp_address - base_address)(%rsi, 1) + +jmp_address: + .long 0 /* address */ + .word 0x10 /* segment */ .code32 pm_code: - /* Disable IA-32e mode by clearing IA32_EFER.LME */ - xorl %eax, %eax - xorl %edx, %edx - movl $MSR_EFER, %ecx - wrmsr + /* cs segment has been updated, now update the rest */ + mov $0x18, %eax + mov %eax, %ds + mov %eax, %es + mov %eax, %fs + mov %eax, %gs + mov %eax, %ss - /* Turn off paging to disable long mode */ - movl %cr0, %eax - andl $~CR0_PG_FLAG, %eax - movl %eax, %cr0 + /* disable paging. */ + mov %cr0, %eax + btr $CR0_PG_BIT, %eax /* PG in CR0 */ + mov %eax, %cr0 + + /* disable long mode. */ + mov $MSR_EFER, %ecx + rdmsr + btr $8, %eax + wrmsr - /* Far return */ - lret + /* kernel jump */ + mov %ebx, %esi + jmp *%edi .code64 .align 4 -- -Gene
Patrick Masotta
2015-Aug-24 14:27 UTC
[syslinux] [PATCH] efi: leaving long mode in kernel_jump routine
> On Tue, Aug 4, 2015 at 2:55 AM, Thomas Letan via Syslinux <syslinux at zytor.com> wrote:> Syslinux 6.03 (efi64) fails to boot a 32-bit kernel. The way Syslinux > leaves long mode in kernel_jump assembly routine does not follow AMD64 > specifications. More precisely: > 1. After setting a new GADT, `cs` has to be refresh by doing a long > jump, but it is not > 2. Other segments have to be updated, but they are not > 3. Disabling paging has to be done before disabling long mode, but the > implementation does the opposite > > In most cases, a computer that tries to execute the kernel_jump routine > reboot (it can also hangs). This patch fixes the kernel_jump routine. > > Signed-off-by: Thomas Letan <thomas.letan at ssi.gouv.fr> > --- >? efi/x86_64/linux.S | 61 > ++++++++++++++++++++++++++++++++++++------------------ >? 1 file changed, 41 insertions(+), 20 deletions(-) > I can just confirm this patch: 1) it is needed. 2) it solves the problem. 3) so far not regression detected. Test: Client Hardware: HP 2570P Boot Mode: PXE EFI64 Target OS: ubuntu-15.04-desktop-i386.iso Trying to PXE boot "ubuntu-15.04-desktop-i386.iso" on an EFI64 platform quietly aborts after transferring initrd.lz. After applying the present patch "ubuntu-15.04-desktop-i386.iso" booted normally. The normal booting of AMD64 based OSs looks unaffected by the patch. Best, Patrick
Celelibi
2015-Aug-31 19:38 UTC
[syslinux] [PATCH] efi: leaving long mode in kernel_jump routine
2015-08-23 20:09 UTC+02:00, Gene Cumm via Syslinux <syslinux at zytor.com>:> On Tue, Aug 4, 2015 at 2:55 AM, Thomas Letan via Syslinux > <syslinux at zytor.com> wrote: >> Syslinux 6.03 (efi64) fails to boot a 32-bit kernel. The way Syslinux >> leaves long mode in kernel_jump assembly routine does not follow AMD64 >> specifications. More precisely: >> 1. After setting a new GADT, `cs` has to be refresh by doing a long >> jump, but it is not >> 2. Other segments have to be updated, but they are not >> 3. Disabling paging has to be done before disabling long mode, but the >> implementation does the opposite >> >> In most cases, a computer that tries to execute the kernel_jump routine >> reboot (it can also hangs). This patch fixes the kernel_jump routine. >> >> Signed-off-by: Thomas Letan <thomas.letan at ssi.gouv.fr> > > Thomas, I noticed you created bug ID 62 ( > http://bugzilla.syslinux.org/show_bug.cgi?id=62 ) which appears to be > a duplicate of bug 38 ( > http://bugzilla.syslinux.org/show_bug.cgi?id=38 ). > > Is there any reason this isn't a duplicate?I don't know if there is a reason beyond "I didn't see". Anyway, this patch fixes bug #38 for me. So both could be closed at the same time. Celelibi
Gene Cumm
2015-Sep-08 10:13 UTC
[syslinux] [PATCH] efi: leaving long mode in kernel_jump routine
On Tue, Aug 4, 2015 at 2:55 AM, Thomas Letan via Syslinux <syslinux at zytor.com> wrote:> Syslinux 6.03 (efi64) fails to boot a 32-bit kernel. The way Syslinux > leaves long mode in kernel_jump assembly routine does not follow AMD64 > specifications. More precisely: > 1. After setting a new GADT, `cs` has to be refresh by doing a long > jump, but it is not > 2. Other segments have to be updated, but they are not > 3. Disabling paging has to be done before disabling long mode, but the > implementation does the opposite > > In most cases, a computer that tries to execute the kernel_jump routine > reboot (it can also hangs). This patch fixes the kernel_jump routine. > > Signed-off-by: Thomas Letan <thomas.letan at ssi.gouv.fr>Merged. Thanks. -- -Gene
Thomas Letan
2015-Sep-15 07:37 UTC
[syslinux] [PATCH] efi: leaving long mode in kernel_jump routine
Thanks! And sorry I didn't have the time to test more? Le 08/09/2015 12:13, Gene Cumm a ?crit :> On Tue, Aug 4, 2015 at 2:55 AM, Thomas Letan via Syslinux > <syslinux at zytor.com> wrote: >> Syslinux 6.03 (efi64) fails to boot a 32-bit kernel. The way Syslinux >> leaves long mode in kernel_jump assembly routine does not follow AMD64 >> specifications. More precisely: >> 1. After setting a new GADT, `cs` has to be refresh by doing a long >> jump, but it is not >> 2. Other segments have to be updated, but they are not >> 3. Disabling paging has to be done before disabling long mode, but the >> implementation does the opposite >> >> In most cases, a computer that tries to execute the kernel_jump routine >> reboot (it can also hangs). This patch fixes the kernel_jump routine. >> >> Signed-off-by: Thomas Letan <thomas.letan at ssi.gouv.fr> > > Merged. Thanks. >