Hi,
I am using grub-1.99 on a x86 platform. The BIOS is originated from AMI and customized a little by our company. When booting up Linux 3.X, I can see the error message "couldn't terminate EFI services".
According to the colaberate debug by both SW/FW side, it is found that before grub calls EFI API exit_boot_services(), the internal finish_key is changed. See the following code and notes for details.
Code from GRUB-1.99
=====================================
if (grub_efi_get_memory_map (&finish_mmap_size, finish_mmap_buf, &finish_key,
&finish_desc_size, &finish_desc_version) < 0)
return grub_error (GRUB_ERR_IO, "couldn't retrieve memory map");
if (outbuf && *outbuf_size < finish_mmap_size)
return grub_error (GRUB_ERR_IO, "memory map buffer is too small");
finish_mmap_buf = grub_malloc (finish_mmap_size);
if (!finish_mmap_buf)
return grub_errno;
if (grub_efi_get_memory_map (&finish_mmap_size, finish_mmap_buf, &finish_key,
&finish_desc_size, &finish_desc_version) <= 0) <== see note 1
return grub_error (GRUB_ERR_IO, "couldn't retrieve memory map");
b = grub_efi_system_table->boot_services;
status = efi_call_2 (b->exit_boot_services, grub_efi_image_handle, <== see note 2
finish_key);
if (status != GRUB_EFI_SUCCESS)
return grub_error (GRUB_ERR_IO, "couldn't terminate EFI services");
Note 1:
BIOS returns the memory map and the finish_key = X
Note 2:
BIOS changes the memory map and the internal finish_key becomes X+1, so the input finish_key does not match the interna key, the API call fails.
======================================
Since the BIOS core is from AMI we cannot correct the FW behavior so we did a small modification to the GRUB (see below). We retry the exit boot service API and this solves the problem.
Please consider if this modification is reasonable for future grub releases.
PS. I also checked the latest grub-2.00 code however this part of code is the same as grub-1.99, so grub upgrade cannot help.
do {
if (grub_efi_get_memory_map (&finish_mmap_size, finish_mmap_buf, &finish_key,
&finish_desc_size, &finish_desc_version) < 0)
return grub_error (GRUB_ERR_IO, "couldn't retrieve memory map");
if (outbuf && *outbuf_size < finish_mmap_size)
return grub_error (GRUB_ERR_IO, "memory map buffer is too small");
finish_mmap_buf = grub_malloc (finish_mmap_size);
if (!finish_mmap_buf)
return grub_errno;
if (grub_efi_get_memory_map (&finish_mmap_size, finish_mmap_buf, &finish_key,
&finish_desc_size, &finish_desc_version) <= 0)
return grub_error (GRUB_ERR_IO, "couldn't retrieve memory map");
b = grub_efi_system_table->boot_services;
status = efi_call_2 (b->exit_boot_services, grub_efi_image_handle,
finish_key);
if (status != GRUB_EFI_SUCCESS) {
grub_free(finish_mmap_buf);
}
} while (status != GRUB_EFI_SUCCESS && retry++ < 2); <== retry calling exit boot service
if (status != GRUB_EFI_SUCCESS)
return grub_error (GRUB_ERR_IO, "couldn't terminate EFI services");
Thanks,
- Weber Wang