[SeaBIOS] varlow/extrastack vs code
Laszlo Ersek
lersek at redhat.com
Tue Feb 14 17:36:34 CET 2017
On 02/14/17 17:22, Kevin O'Connor wrote:
> On Tue, Feb 14, 2017 at 02:50:23PM +0000, Dr. David Alan Gilbert wrote:
>> I've been sporadically carrying on debugging this and I
>> think I have a bit more understanding, but not quite all the way.
>>
>> I'm pretty sure we are trying to run code that's been over written
>> by variable data - which I believe to be TimerLast, and I'm also
>> fairly sure the problem is not a delayed reset from multiple reboot signals.
>>
>> Some detail:
>> a) I added some debug to qemu to print what triggered the reboot by
>> always passing qemu_system_reset_request a string; the kernel
>> is always rebooting via KBD_CCMD_RESET, I see that enter the BIOS
>> and then see SeaBIOS then does KBD_CCMD_RESET again - which I think
>> is the one coming from handle_resume32.
>> But then I noticed that every so often we got a reboot from a KVM_SHUTDOWN
>> which is apparently a triple-fault or the like.
>>
>> b) With a KVM trace I got the addresses the fault was happening at,
>> and it was always an address in the copy fo the bios around bffbcf..
>> although the actual failure jumped about a bit, taking the:
>> Relocating init from 0x000e4810 to 0xbffb2050 (size 57120)
>> I worked the fault address back to 'ef799' and from the objdump
>> found that was an address in maininit but also corresponded to
>> TimerLast:
>
> I can confirm there is a defect with TimerLast and reboot handling.
> I'm not sure what the best fix is - suggestions welcome.
>
> The root of the problem is that QEMU does not reset the f-segment on a
> reboot signal
Are you sure this applies to recent QEMU too, not just ancient QEMU?
> and so SeaBIOS must manually reset that memory.
Do you mean a workaround for the (historically lacking) emulation of the
PAM chipset registers? PAM emulation has been correct for quite a while
now in QEMU, as far as I can tell.
(Sorry if I'm completely off.)
Thanks,
Laszlo
> It does
> this in the call to qemu_prep_reset() in tryReboot(). That call
> copies the pristine copy of the bios at 0xffff0000 to 0xf0000. The
> code then goes on to signal a hard reboot so that all the hardware is
> reset and so that the code starts just like it would on first boot.
>
> Unfortunately, the hard reboot logic makes calls to udelay() -
> specifically in i8042_reboot() and pci_reboot(). These calls to
> udelay() are not valid here as the timer isn't even necessarily
> configured at this point. Alas - it also has the nasty possibility of
> corrupting the pristine copy of the bios that was just made in
> qemu_prep_reset(). Random crashes can then occur on the next boot.
>
> It's easy to catch this with the following code change:
>
> --- a/src/hw/timer.c
> +++ b/src/hw/timer.c
> @@ -146,6 +146,8 @@ timer_adjust_bits(u32 value, u32 validbits)
> static u32
> timer_read(void)
> {
> + if (!GET_GLOBAL(HaveRunPost))
> + panic("timer read too early\n");
> u16 port = GET_GLOBAL(TimerPort);
> if (CONFIG_TSC_TIMER && !port)
> // Read from CPU TSC
>
> With that, I get a panic on every reboot. Commenting out the udelay()
> calls then gets reboots working again.
>
> The simplest fix is to remove the udelay() calls from the reboot
> logic. However, it's not clear to me if it's valid to omit the delays
> when running on real hardware. Another possibility would be to try
> and harden timer_read() so that it can be run very early (prior to
> code relocation), but that's tricky to implement correctly..
>
> Good find Dave - I know that wasn't easy to track down.
> -Kevin
>
More information about the SeaBIOS
mailing list