* Refactor the EFI memory map code into architecture neutral files
and allow drivers to permanently reserve EFI boot services regions on x86, as well as ARM/arm64 - Matt Fleming * Add ARM support for the EFI esrt driver - Ard Biesheuvel * Make the EFI runtime services and efivar API interruptible by swapping spinlocks for semaphores - Sylvain Chouleur * Provide the EFI identity mapping for kexec which allows kexec to work on SGI/UV platforms with requiring the "noefi" kernel command line parameter - Alex Thorlton * Add debugfs node to dump EFI page tables on arm64 - Ard Biesheuvel * Merge the EFI test driver being carried out of tree until now in the FWTS project - Ivan Hu * Expand the list of flags for classifying EFI regions as "RAM" on arm64 so we align with the UEFI spec - Ard Biesheuvel * Optimise out the EFI mixed mode if it's unsupported (CONFIG_X86_32) or disabled (CONFIG_EFI_MIXED=n) and switch the early EFI boot services function table for direct calls, alleviating us from having to maintain the custom function table - Lukas Wunner * Miscellaneous cleanups and fixes -----BEGIN PGP SIGNATURE----- iQI2BAABCAAgBQJX0tCTGRxtYXR0QGNvZGVibHVlcHJpbnQuY28udWsACgkQLzhZ wI0jPVWLVBAAn/iM91Vmhggdk3t0wCMJzrNGonw61VJ9TZJVbCUJyiH0qdDUThhj R4rO+6Vf6yOuyswu+mGmae61tfsjwJHH+IPpB8nRLIGQRwzoxk+aGC7FzmQ0ISVO wIdv5shsmeWhFAyNB1D4hzlp1NxOZaqcU/0cfUVGe4HmK0Js3tUpWWx8VgJ7yvW+ X1PBbfyChArGqiwV6FJz/mJxRAgByUfhvYMcX9HhQkou6F4U5Y8l3vlhUMbuAZAi ZfG2LWGYCQ+F4XKxMq2QDAtdUgBzlYWw6W60o55x9WO4cEVSzNVRgedto5o1Zea9 2QGEr94gim+e5cJ/HeDIEmbWZhAqIdcNDqXSSBd1CDVQytp4PNAn6rxk+2S9kxoe T9Mk523HEabo+AZvDAPPJlzcsnIe83JYy69M1xFvcP25ebk7y2BwQtd1jwWPrPDQ Q/llzF93aezUFR/guvIw0oHckhQl0ZkNedL9Tq4+UKL0ibp2X4gSX636/x4PkBSP 5+pyfmO1SAqTiiMQGQMnp4+ngPQeQrxkmVnh1P7cKlTNXg1IoS03t46Xn2Pj10cd 3KneVDeN9DKIAOn7wPKuPnjTho+9FH36xbwTaIgbt0cWuFFfu090rmqOQfjAJEDN foHzsMZ7S6CmeOJnj97NNR8sMQDcc+p9bh1KXpJIHaZAgrKmvqPZpMk= =G7L6 -----END PGP SIGNATURE----- Merge tag 'efi-next' of git://git.kernel.org/pub/scm/linux/kernel/git/mfleming/efi into efi/core Pull EFI updates from Matt Fleming: "* Refactor the EFI memory map code into architecture neutral files and allow drivers to permanently reserve EFI boot services regions on x86, as well as ARM/arm64 - Matt Fleming * Add ARM support for the EFI esrt driver - Ard Biesheuvel * Make the EFI runtime services and efivar API interruptible by swapping spinlocks for semaphores - Sylvain Chouleur * Provide the EFI identity mapping for kexec which allows kexec to work on SGI/UV platforms with requiring the "noefi" kernel command line parameter - Alex Thorlton * Add debugfs node to dump EFI page tables on arm64 - Ard Biesheuvel * Merge the EFI test driver being carried out of tree until now in the FWTS project - Ivan Hu * Expand the list of flags for classifying EFI regions as "RAM" on arm64 so we align with the UEFI spec - Ard Biesheuvel * Optimise out the EFI mixed mode if it's unsupported (CONFIG_X86_32) or disabled (CONFIG_EFI_MIXED=n) and switch the early EFI boot services function table for direct calls, alleviating us from having to maintain the custom function table - Lukas Wunner * Miscellaneous cleanups and fixes" Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
5465fe0fc3
|
@ -4583,6 +4583,13 @@ M: Peter Jones <pjones@redhat.com>
|
|||
S: Maintained
|
||||
F: drivers/video/fbdev/efifb.c
|
||||
|
||||
EFI TEST DRIVER
|
||||
L: linux-efi@vger.kernel.org
|
||||
M: Ivan Hu <ivan.hu@canonical.com>
|
||||
M: Matt Fleming <matt@codeblueprint.co.uk>
|
||||
S: Maintained
|
||||
F: drivers/firmware/efi/test/
|
||||
|
||||
EFS FILESYSTEM
|
||||
W: http://aeschi.ch.eu.org/efs/
|
||||
S: Orphan
|
||||
|
|
|
@ -29,22 +29,11 @@ __pure const struct efi_config *__efi_early(void)
|
|||
static void setup_boot_services##bits(struct efi_config *c) \
|
||||
{ \
|
||||
efi_system_table_##bits##_t *table; \
|
||||
efi_boot_services_##bits##_t *bt; \
|
||||
\
|
||||
table = (typeof(table))sys_table; \
|
||||
\
|
||||
c->boot_services = table->boottime; \
|
||||
c->text_output = table->con_out; \
|
||||
\
|
||||
bt = (typeof(bt))(unsigned long)(table->boottime); \
|
||||
\
|
||||
c->allocate_pool = bt->allocate_pool; \
|
||||
c->allocate_pages = bt->allocate_pages; \
|
||||
c->get_memory_map = bt->get_memory_map; \
|
||||
c->free_pool = bt->free_pool; \
|
||||
c->free_pages = bt->free_pages; \
|
||||
c->locate_handle = bt->locate_handle; \
|
||||
c->handle_protocol = bt->handle_protocol; \
|
||||
c->exit_boot_services = bt->exit_boot_services; \
|
||||
}
|
||||
BOOT_SERVICES(32);
|
||||
BOOT_SERVICES(64);
|
||||
|
@ -286,29 +275,6 @@ void efi_char16_printk(efi_system_table_t *table, efi_char16_t *str)
|
|||
}
|
||||
}
|
||||
|
||||
static void find_bits(unsigned long mask, u8 *pos, u8 *size)
|
||||
{
|
||||
u8 first, len;
|
||||
|
||||
first = 0;
|
||||
len = 0;
|
||||
|
||||
if (mask) {
|
||||
while (!(mask & 0x1)) {
|
||||
mask = mask >> 1;
|
||||
first++;
|
||||
}
|
||||
|
||||
while (mask & 0x1) {
|
||||
mask = mask >> 1;
|
||||
len++;
|
||||
}
|
||||
}
|
||||
|
||||
*pos = first;
|
||||
*size = len;
|
||||
}
|
||||
|
||||
static efi_status_t
|
||||
__setup_efi_pci32(efi_pci_io_protocol_32 *pci, struct pci_setup_rom **__rom)
|
||||
{
|
||||
|
@ -578,7 +544,7 @@ setup_uga32(void **uga_handle, unsigned long size, u32 *width, u32 *height)
|
|||
efi_guid_t uga_proto = EFI_UGA_PROTOCOL_GUID;
|
||||
unsigned long nr_ugas;
|
||||
u32 *handles = (u32 *)uga_handle;;
|
||||
efi_status_t status;
|
||||
efi_status_t status = EFI_INVALID_PARAMETER;
|
||||
int i;
|
||||
|
||||
first_uga = NULL;
|
||||
|
@ -623,7 +589,7 @@ setup_uga64(void **uga_handle, unsigned long size, u32 *width, u32 *height)
|
|||
efi_guid_t uga_proto = EFI_UGA_PROTOCOL_GUID;
|
||||
unsigned long nr_ugas;
|
||||
u64 *handles = (u64 *)uga_handle;;
|
||||
efi_status_t status;
|
||||
efi_status_t status = EFI_INVALID_PARAMETER;
|
||||
int i;
|
||||
|
||||
first_uga = NULL;
|
||||
|
|
|
@ -82,7 +82,7 @@ ENTRY(efi_pe_entry)
|
|||
|
||||
/* Relocate efi_config->call() */
|
||||
leal efi32_config(%esi), %eax
|
||||
add %esi, 88(%eax)
|
||||
add %esi, 32(%eax)
|
||||
pushl %eax
|
||||
|
||||
call make_boot_params
|
||||
|
@ -108,7 +108,7 @@ ENTRY(efi32_stub_entry)
|
|||
|
||||
/* Relocate efi_config->call() */
|
||||
leal efi32_config(%esi), %eax
|
||||
add %esi, 88(%eax)
|
||||
add %esi, 32(%eax)
|
||||
pushl %eax
|
||||
2:
|
||||
call efi_main
|
||||
|
@ -264,7 +264,7 @@ relocated:
|
|||
#ifdef CONFIG_EFI_STUB
|
||||
.data
|
||||
efi32_config:
|
||||
.fill 11,8,0
|
||||
.fill 4,8,0
|
||||
.long efi_call_phys
|
||||
.long 0
|
||||
.byte 0
|
||||
|
|
|
@ -265,7 +265,7 @@ ENTRY(efi_pe_entry)
|
|||
/*
|
||||
* Relocate efi_config->call().
|
||||
*/
|
||||
addq %rbp, efi64_config+88(%rip)
|
||||
addq %rbp, efi64_config+32(%rip)
|
||||
|
||||
movq %rax, %rdi
|
||||
call make_boot_params
|
||||
|
@ -285,7 +285,7 @@ handover_entry:
|
|||
* Relocate efi_config->call().
|
||||
*/
|
||||
movq efi_config(%rip), %rax
|
||||
addq %rbp, 88(%rax)
|
||||
addq %rbp, 32(%rax)
|
||||
2:
|
||||
movq efi_config(%rip), %rdi
|
||||
call efi_main
|
||||
|
@ -457,14 +457,14 @@ efi_config:
|
|||
#ifdef CONFIG_EFI_MIXED
|
||||
.global efi32_config
|
||||
efi32_config:
|
||||
.fill 11,8,0
|
||||
.fill 4,8,0
|
||||
.quad efi64_thunk
|
||||
.byte 0
|
||||
#endif
|
||||
|
||||
.global efi64_config
|
||||
efi64_config:
|
||||
.fill 11,8,0
|
||||
.fill 4,8,0
|
||||
.quad efi_call
|
||||
.byte 1
|
||||
#endif /* CONFIG_EFI_STUB */
|
||||
|
|
|
@ -117,7 +117,6 @@ extern int __init efi_memblock_x86_reserve_range(void);
|
|||
extern pgd_t * __init efi_call_phys_prolog(void);
|
||||
extern void __init efi_call_phys_epilog(pgd_t *save_pgd);
|
||||
extern void __init efi_print_memmap(void);
|
||||
extern void __init efi_unmap_memmap(void);
|
||||
extern void __init efi_memory_uc(u64 addr, unsigned long size);
|
||||
extern void __init efi_map_region(efi_memory_desc_t *md);
|
||||
extern void __init efi_map_region_fixed(efi_memory_desc_t *md);
|
||||
|
@ -192,14 +191,7 @@ static inline efi_status_t efi_thunk_set_virtual_address_map(
|
|||
struct efi_config {
|
||||
u64 image_handle;
|
||||
u64 table;
|
||||
u64 allocate_pool;
|
||||
u64 allocate_pages;
|
||||
u64 get_memory_map;
|
||||
u64 free_pool;
|
||||
u64 free_pages;
|
||||
u64 locate_handle;
|
||||
u64 handle_protocol;
|
||||
u64 exit_boot_services;
|
||||
u64 boot_services;
|
||||
u64 text_output;
|
||||
efi_status_t (*call)(unsigned long, ...);
|
||||
bool is64;
|
||||
|
@ -207,14 +199,27 @@ struct efi_config {
|
|||
|
||||
__pure const struct efi_config *__efi_early(void);
|
||||
|
||||
static inline bool efi_is_64bit(void)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_X86_64))
|
||||
return false;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_EFI_MIXED))
|
||||
return true;
|
||||
|
||||
return __efi_early()->is64;
|
||||
}
|
||||
|
||||
#define efi_call_early(f, ...) \
|
||||
__efi_early()->call(__efi_early()->f, __VA_ARGS__);
|
||||
__efi_early()->call(efi_is_64bit() ? \
|
||||
((efi_boot_services_64_t *)(unsigned long) \
|
||||
__efi_early()->boot_services)->f : \
|
||||
((efi_boot_services_32_t *)(unsigned long) \
|
||||
__efi_early()->boot_services)->f, __VA_ARGS__)
|
||||
|
||||
#define __efi_call_early(f, ...) \
|
||||
__efi_early()->call((unsigned long)f, __VA_ARGS__);
|
||||
|
||||
#define efi_is_64bit() __efi_early()->is64
|
||||
|
||||
extern bool efi_reboot_required(void);
|
||||
|
||||
#else
|
||||
|
|
|
@ -1096,19 +1096,19 @@ void __init setup_arch(char **cmdline_p)
|
|||
memblock_set_current_limit(ISA_END_ADDRESS);
|
||||
memblock_x86_fill();
|
||||
|
||||
if (efi_enabled(EFI_BOOT)) {
|
||||
efi_fake_memmap();
|
||||
efi_find_mirror();
|
||||
}
|
||||
|
||||
reserve_bios_regions();
|
||||
|
||||
/*
|
||||
* The EFI specification says that boot service code won't be called
|
||||
* after ExitBootServices(). This is, in fact, a lie.
|
||||
*/
|
||||
if (efi_enabled(EFI_MEMMAP))
|
||||
if (efi_enabled(EFI_MEMMAP)) {
|
||||
efi_fake_memmap();
|
||||
efi_find_mirror();
|
||||
efi_esrt_init();
|
||||
|
||||
/*
|
||||
* The EFI specification says that boot service code won't be
|
||||
* called after ExitBootServices(). This is, in fact, a lie.
|
||||
*/
|
||||
efi_reserve_boot_services();
|
||||
}
|
||||
|
||||
/* preallocate 4k for mptable mpc */
|
||||
early_reserve_e820_mpc_new();
|
||||
|
|
|
@ -82,21 +82,12 @@ void __init efi_bgrt_init(void)
|
|||
}
|
||||
bgrt_image_size = bmp_header.size;
|
||||
|
||||
bgrt_image = kmalloc(bgrt_image_size, GFP_KERNEL | __GFP_NOWARN);
|
||||
bgrt_image = memremap(bgrt_tab->image_address, bmp_header.size, MEMREMAP_WB);
|
||||
if (!bgrt_image) {
|
||||
pr_notice("Ignoring BGRT: failed to allocate memory for image (wanted %zu bytes)\n",
|
||||
bgrt_image_size);
|
||||
return;
|
||||
}
|
||||
|
||||
image = memremap(bgrt_tab->image_address, bmp_header.size, MEMREMAP_WB);
|
||||
if (!image) {
|
||||
pr_notice("Ignoring BGRT: failed to map image memory\n");
|
||||
kfree(bgrt_image);
|
||||
bgrt_image = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(bgrt_image, image, bgrt_image_size);
|
||||
memunmap(image);
|
||||
efi_mem_reserve(bgrt_tab->image_address, bgrt_image_size);
|
||||
}
|
||||
|
|
|
@ -172,7 +172,9 @@ static void __init do_add_efi_memmap(void)
|
|||
int __init efi_memblock_x86_reserve_range(void)
|
||||
{
|
||||
struct efi_info *e = &boot_params.efi_info;
|
||||
struct efi_memory_map_data data;
|
||||
phys_addr_t pmap;
|
||||
int rv;
|
||||
|
||||
if (efi_enabled(EFI_PARAVIRT))
|
||||
return 0;
|
||||
|
@ -187,11 +189,17 @@ int __init efi_memblock_x86_reserve_range(void)
|
|||
#else
|
||||
pmap = (e->efi_memmap | ((__u64)e->efi_memmap_hi << 32));
|
||||
#endif
|
||||
efi.memmap.phys_map = pmap;
|
||||
efi.memmap.nr_map = e->efi_memmap_size /
|
||||
e->efi_memdesc_size;
|
||||
efi.memmap.desc_size = e->efi_memdesc_size;
|
||||
efi.memmap.desc_version = e->efi_memdesc_version;
|
||||
data.phys_map = pmap;
|
||||
data.size = e->efi_memmap_size;
|
||||
data.desc_size = e->efi_memdesc_size;
|
||||
data.desc_version = e->efi_memdesc_version;
|
||||
|
||||
rv = efi_memmap_init_early(&data);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
if (add_efi_memmap)
|
||||
do_add_efi_memmap();
|
||||
|
||||
WARN(efi.memmap.desc_version != 1,
|
||||
"Unexpected EFI_MEMORY_DESCRIPTOR version %ld",
|
||||
|
@ -218,19 +226,6 @@ void __init efi_print_memmap(void)
|
|||
}
|
||||
}
|
||||
|
||||
void __init efi_unmap_memmap(void)
|
||||
{
|
||||
unsigned long size;
|
||||
|
||||
clear_bit(EFI_MEMMAP, &efi.flags);
|
||||
|
||||
size = efi.memmap.nr_map * efi.memmap.desc_size;
|
||||
if (efi.memmap.map) {
|
||||
early_memunmap(efi.memmap.map, size);
|
||||
efi.memmap.map = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int __init efi_systab_init(void *phys)
|
||||
{
|
||||
if (efi_enabled(EFI_64BIT)) {
|
||||
|
@ -414,33 +409,6 @@ static int __init efi_runtime_init(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __init efi_memmap_init(void)
|
||||
{
|
||||
unsigned long addr, size;
|
||||
|
||||
if (efi_enabled(EFI_PARAVIRT))
|
||||
return 0;
|
||||
|
||||
/* Map the EFI memory map */
|
||||
size = efi.memmap.nr_map * efi.memmap.desc_size;
|
||||
addr = (unsigned long)efi.memmap.phys_map;
|
||||
|
||||
efi.memmap.map = early_memremap(addr, size);
|
||||
if (efi.memmap.map == NULL) {
|
||||
pr_err("Could not map the memory map!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
efi.memmap.map_end = efi.memmap.map + size;
|
||||
|
||||
if (add_efi_memmap)
|
||||
do_add_efi_memmap();
|
||||
|
||||
set_bit(EFI_MEMMAP, &efi.flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init efi_init(void)
|
||||
{
|
||||
efi_char16_t *c16;
|
||||
|
@ -498,16 +466,14 @@ void __init efi_init(void)
|
|||
if (!efi_runtime_supported())
|
||||
pr_info("No EFI runtime due to 32/64-bit mismatch with kernel\n");
|
||||
else {
|
||||
if (efi_runtime_disabled() || efi_runtime_init())
|
||||
if (efi_runtime_disabled() || efi_runtime_init()) {
|
||||
efi_memmap_unmap();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (efi_memmap_init())
|
||||
return;
|
||||
|
||||
if (efi_enabled(EFI_DBG))
|
||||
efi_print_memmap();
|
||||
|
||||
efi_esrt_init();
|
||||
}
|
||||
|
||||
void __init efi_late_init(void)
|
||||
|
@ -624,42 +590,6 @@ static void __init get_systab_virt_addr(efi_memory_desc_t *md)
|
|||
}
|
||||
}
|
||||
|
||||
static void __init save_runtime_map(void)
|
||||
{
|
||||
#ifdef CONFIG_KEXEC_CORE
|
||||
unsigned long desc_size;
|
||||
efi_memory_desc_t *md;
|
||||
void *tmp, *q = NULL;
|
||||
int count = 0;
|
||||
|
||||
if (efi_enabled(EFI_OLD_MEMMAP))
|
||||
return;
|
||||
|
||||
desc_size = efi.memmap.desc_size;
|
||||
|
||||
for_each_efi_memory_desc(md) {
|
||||
if (!(md->attribute & EFI_MEMORY_RUNTIME) ||
|
||||
(md->type == EFI_BOOT_SERVICES_CODE) ||
|
||||
(md->type == EFI_BOOT_SERVICES_DATA))
|
||||
continue;
|
||||
tmp = krealloc(q, (count + 1) * desc_size, GFP_KERNEL);
|
||||
if (!tmp)
|
||||
goto out;
|
||||
q = tmp;
|
||||
|
||||
memcpy(q + count * desc_size, md, desc_size);
|
||||
count++;
|
||||
}
|
||||
|
||||
efi_runtime_map_setup(q, count, desc_size);
|
||||
return;
|
||||
|
||||
out:
|
||||
kfree(q);
|
||||
pr_err("Error saving runtime map, efi runtime on kexec non-functional!!\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void *realloc_pages(void *old_memmap, int old_shift)
|
||||
{
|
||||
void *ret;
|
||||
|
@ -745,6 +675,46 @@ static void *efi_map_next_entry(void *entry)
|
|||
return entry;
|
||||
}
|
||||
|
||||
static bool should_map_region(efi_memory_desc_t *md)
|
||||
{
|
||||
/*
|
||||
* Runtime regions always require runtime mappings (obviously).
|
||||
*/
|
||||
if (md->attribute & EFI_MEMORY_RUNTIME)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* 32-bit EFI doesn't suffer from the bug that requires us to
|
||||
* reserve boot services regions, and mixed mode support
|
||||
* doesn't exist for 32-bit kernels.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_X86_32))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Map all of RAM so that we can access arguments in the 1:1
|
||||
* mapping when making EFI runtime calls.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_EFI_MIXED) && !efi_is_native()) {
|
||||
if (md->type == EFI_CONVENTIONAL_MEMORY ||
|
||||
md->type == EFI_LOADER_DATA ||
|
||||
md->type == EFI_LOADER_CODE)
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Map boot services regions as a workaround for buggy
|
||||
* firmware that accesses them even when they shouldn't.
|
||||
*
|
||||
* See efi_{reserve,free}_boot_services().
|
||||
*/
|
||||
if (md->type == EFI_BOOT_SERVICES_CODE ||
|
||||
md->type == EFI_BOOT_SERVICES_DATA)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Map the efi memory ranges of the runtime services and update new_mmap with
|
||||
* virtual addresses.
|
||||
|
@ -761,13 +731,9 @@ static void * __init efi_map_regions(int *count, int *pg_shift)
|
|||
p = NULL;
|
||||
while ((p = efi_map_next_entry(p))) {
|
||||
md = p;
|
||||
if (!(md->attribute & EFI_MEMORY_RUNTIME)) {
|
||||
#ifdef CONFIG_X86_64
|
||||
if (md->type != EFI_BOOT_SERVICES_CODE &&
|
||||
md->type != EFI_BOOT_SERVICES_DATA)
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!should_map_region(md))
|
||||
continue;
|
||||
|
||||
efi_map_region(md);
|
||||
get_systab_virt_addr(md);
|
||||
|
@ -803,7 +769,7 @@ static void __init kexec_enter_virtual_mode(void)
|
|||
* non-native EFI
|
||||
*/
|
||||
if (!efi_is_native()) {
|
||||
efi_unmap_memmap();
|
||||
efi_memmap_unmap();
|
||||
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
|
||||
return;
|
||||
}
|
||||
|
@ -823,7 +789,18 @@ static void __init kexec_enter_virtual_mode(void)
|
|||
get_systab_virt_addr(md);
|
||||
}
|
||||
|
||||
save_runtime_map();
|
||||
/*
|
||||
* Unregister the early EFI memmap from efi_init() and install
|
||||
* the new EFI memory map.
|
||||
*/
|
||||
efi_memmap_unmap();
|
||||
|
||||
if (efi_memmap_init_late(efi.memmap.phys_map,
|
||||
efi.memmap.desc_size * efi.memmap.nr_map)) {
|
||||
pr_err("Failed to remap late EFI memory map\n");
|
||||
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
|
||||
return;
|
||||
}
|
||||
|
||||
BUG_ON(!efi.systab);
|
||||
|
||||
|
@ -884,6 +861,7 @@ static void __init __efi_enter_virtual_mode(void)
|
|||
int count = 0, pg_shift = 0;
|
||||
void *new_memmap = NULL;
|
||||
efi_status_t status;
|
||||
phys_addr_t pa;
|
||||
|
||||
efi.systab = NULL;
|
||||
|
||||
|
@ -901,11 +879,24 @@ static void __init __efi_enter_virtual_mode(void)
|
|||
return;
|
||||
}
|
||||
|
||||
save_runtime_map();
|
||||
pa = __pa(new_memmap);
|
||||
|
||||
/*
|
||||
* Unregister the early EFI memmap from efi_init() and install
|
||||
* the new EFI memory map that we are about to pass to the
|
||||
* firmware via SetVirtualAddressMap().
|
||||
*/
|
||||
efi_memmap_unmap();
|
||||
|
||||
if (efi_memmap_init_late(pa, efi.memmap.desc_size * count)) {
|
||||
pr_err("Failed to remap late EFI memory map\n");
|
||||
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
|
||||
return;
|
||||
}
|
||||
|
||||
BUG_ON(!efi.systab);
|
||||
|
||||
if (efi_setup_page_tables(__pa(new_memmap), 1 << pg_shift)) {
|
||||
if (efi_setup_page_tables(pa, 1 << pg_shift)) {
|
||||
clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
|
||||
return;
|
||||
}
|
||||
|
@ -917,14 +908,14 @@ static void __init __efi_enter_virtual_mode(void)
|
|||
efi.memmap.desc_size * count,
|
||||
efi.memmap.desc_size,
|
||||
efi.memmap.desc_version,
|
||||
(efi_memory_desc_t *)__pa(new_memmap));
|
||||
(efi_memory_desc_t *)pa);
|
||||
} else {
|
||||
status = efi_thunk_set_virtual_address_map(
|
||||
efi_phys.set_virtual_address_map,
|
||||
efi.memmap.desc_size * count,
|
||||
efi.memmap.desc_size,
|
||||
efi.memmap.desc_version,
|
||||
(efi_memory_desc_t *)__pa(new_memmap));
|
||||
(efi_memory_desc_t *)pa);
|
||||
}
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
|
@ -956,15 +947,6 @@ static void __init __efi_enter_virtual_mode(void)
|
|||
efi_runtime_update_mappings();
|
||||
efi_dump_pagetable();
|
||||
|
||||
/*
|
||||
* We mapped the descriptor array into the EFI pagetable above
|
||||
* but we're not unmapping it here because if we're running in
|
||||
* EFI mixed mode we need all of memory to be accessible when
|
||||
* we pass parameters to the EFI runtime services in the
|
||||
* thunking code.
|
||||
*/
|
||||
free_pages((unsigned long)new_memmap, pg_shift);
|
||||
|
||||
/* clean DUMMY object */
|
||||
efi_delete_dummy_variable();
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ pgd_t * __init efi_call_phys_prolog(void)
|
|||
early_code_mapping_set_exec(1);
|
||||
|
||||
n_pgds = DIV_ROUND_UP((max_pfn << PAGE_SHIFT), PGDIR_SIZE);
|
||||
save_pgd = kmalloc(n_pgds * sizeof(pgd_t), GFP_KERNEL);
|
||||
save_pgd = kmalloc_array(n_pgds, sizeof(*save_pgd), GFP_KERNEL);
|
||||
|
||||
for (pgd = 0; pgd < n_pgds; pgd++) {
|
||||
save_pgd[pgd] = *pgd_offset_k(pgd * PGDIR_SIZE);
|
||||
|
@ -214,7 +214,6 @@ void efi_sync_low_kernel_mappings(void)
|
|||
int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
|
||||
{
|
||||
unsigned long pfn, text;
|
||||
efi_memory_desc_t *md;
|
||||
struct page *page;
|
||||
unsigned npages;
|
||||
pgd_t *pgd;
|
||||
|
@ -248,25 +247,6 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
|
|||
if (!IS_ENABLED(CONFIG_EFI_MIXED))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Map all of RAM so that we can access arguments in the 1:1
|
||||
* mapping when making EFI runtime calls.
|
||||
*/
|
||||
for_each_efi_memory_desc(md) {
|
||||
if (md->type != EFI_CONVENTIONAL_MEMORY &&
|
||||
md->type != EFI_LOADER_DATA &&
|
||||
md->type != EFI_LOADER_CODE)
|
||||
continue;
|
||||
|
||||
pfn = md->phys_addr >> PAGE_SHIFT;
|
||||
npages = md->num_pages;
|
||||
|
||||
if (kernel_map_pages_in_pgd(pgd, pfn, md->phys_addr, npages, _PAGE_RW)) {
|
||||
pr_err("Failed to map 1:1 memory\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
page = alloc_page(GFP_KERNEL|__GFP_DMA32);
|
||||
if (!page)
|
||||
panic("Unable to allocate EFI runtime stack < 4GB\n");
|
||||
|
@ -359,6 +339,7 @@ void __init efi_map_region(efi_memory_desc_t *md)
|
|||
*/
|
||||
void __init efi_map_region_fixed(efi_memory_desc_t *md)
|
||||
{
|
||||
__map_region(md, md->phys_addr);
|
||||
__map_region(md, md->virt_addr);
|
||||
}
|
||||
|
||||
|
|
|
@ -163,6 +163,71 @@ efi_status_t efi_query_variable_store(u32 attributes, unsigned long size,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(efi_query_variable_store);
|
||||
|
||||
/*
|
||||
* The UEFI specification makes it clear that the operating system is
|
||||
* free to do whatever it wants with boot services code after
|
||||
* ExitBootServices() has been called. Ignoring this recommendation a
|
||||
* significant bunch of EFI implementations continue calling into boot
|
||||
* services code (SetVirtualAddressMap). In order to work around such
|
||||
* buggy implementations we reserve boot services region during EFI
|
||||
* init and make sure it stays executable. Then, after
|
||||
* SetVirtualAddressMap(), it is discarded.
|
||||
*
|
||||
* However, some boot services regions contain data that is required
|
||||
* by drivers, so we need to track which memory ranges can never be
|
||||
* freed. This is done by tagging those regions with the
|
||||
* EFI_MEMORY_RUNTIME attribute.
|
||||
*
|
||||
* Any driver that wants to mark a region as reserved must use
|
||||
* efi_mem_reserve() which will insert a new EFI memory descriptor
|
||||
* into efi.memmap (splitting existing regions if necessary) and tag
|
||||
* it with EFI_MEMORY_RUNTIME.
|
||||
*/
|
||||
void __init efi_arch_mem_reserve(phys_addr_t addr, u64 size)
|
||||
{
|
||||
phys_addr_t new_phys, new_size;
|
||||
struct efi_mem_range mr;
|
||||
efi_memory_desc_t md;
|
||||
int num_entries;
|
||||
void *new;
|
||||
|
||||
if (efi_mem_desc_lookup(addr, &md)) {
|
||||
pr_err("Failed to lookup EFI memory descriptor for %pa\n", &addr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (addr + size > md.phys_addr + (md.num_pages << EFI_PAGE_SHIFT)) {
|
||||
pr_err("Region spans EFI memory descriptors, %pa\n", &addr);
|
||||
return;
|
||||
}
|
||||
|
||||
mr.range.start = addr;
|
||||
mr.range.end = addr + size;
|
||||
mr.attribute = md.attribute | EFI_MEMORY_RUNTIME;
|
||||
|
||||
num_entries = efi_memmap_split_count(&md, &mr.range);
|
||||
num_entries += efi.memmap.nr_map;
|
||||
|
||||
new_size = efi.memmap.desc_size * num_entries;
|
||||
|
||||
new_phys = memblock_alloc(new_size, 0);
|
||||
if (!new_phys) {
|
||||
pr_err("Could not allocate boot services memmap\n");
|
||||
return;
|
||||
}
|
||||
|
||||
new = early_memremap(new_phys, new_size);
|
||||
if (!new) {
|
||||
pr_err("Failed to map new boot services memmap\n");
|
||||
return;
|
||||
}
|
||||
|
||||
efi_memmap_insert(&efi.memmap, new, &mr);
|
||||
early_memunmap(new, new_size);
|
||||
|
||||
efi_memmap_install(new_phys, num_entries);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function for efi_reserve_boot_services() to figure out if we
|
||||
* can free regions in efi_free_boot_services().
|
||||
|
@ -184,15 +249,6 @@ static bool can_free_region(u64 start, u64 size)
|
|||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* The UEFI specification makes it clear that the operating system is free to do
|
||||
* whatever it wants with boot services code after ExitBootServices() has been
|
||||
* called. Ignoring this recommendation a significant bunch of EFI implementations
|
||||
* continue calling into boot services code (SetVirtualAddressMap). In order to
|
||||
* work around such buggy implementations we reserve boot services region during
|
||||
* EFI init and make sure it stays executable. Then, after SetVirtualAddressMap(), it
|
||||
* is discarded.
|
||||
*/
|
||||
void __init efi_reserve_boot_services(void)
|
||||
{
|
||||
efi_memory_desc_t *md;
|
||||
|
@ -249,7 +305,10 @@ void __init efi_reserve_boot_services(void)
|
|||
|
||||
void __init efi_free_boot_services(void)
|
||||
{
|
||||
phys_addr_t new_phys, new_size;
|
||||
efi_memory_desc_t *md;
|
||||
int num_entries = 0;
|
||||
void *new, *new_md;
|
||||
|
||||
for_each_efi_memory_desc(md) {
|
||||
unsigned long long start = md->phys_addr;
|
||||
|
@ -257,12 +316,16 @@ void __init efi_free_boot_services(void)
|
|||
size_t rm_size;
|
||||
|
||||
if (md->type != EFI_BOOT_SERVICES_CODE &&
|
||||
md->type != EFI_BOOT_SERVICES_DATA)
|
||||
md->type != EFI_BOOT_SERVICES_DATA) {
|
||||
num_entries++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Do not free, someone else owns it: */
|
||||
if (md->attribute & EFI_MEMORY_RUNTIME)
|
||||
if (md->attribute & EFI_MEMORY_RUNTIME) {
|
||||
num_entries++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Nasty quirk: if all sub-1MB memory is used for boot
|
||||
|
@ -287,7 +350,41 @@ void __init efi_free_boot_services(void)
|
|||
free_bootmem_late(start, size);
|
||||
}
|
||||
|
||||
efi_unmap_memmap();
|
||||
new_size = efi.memmap.desc_size * num_entries;
|
||||
new_phys = memblock_alloc(new_size, 0);
|
||||
if (!new_phys) {
|
||||
pr_err("Failed to allocate new EFI memmap\n");
|
||||
return;
|
||||
}
|
||||
|
||||
new = memremap(new_phys, new_size, MEMREMAP_WB);
|
||||
if (!new) {
|
||||
pr_err("Failed to map new EFI memmap\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build a new EFI memmap that excludes any boot services
|
||||
* regions that are not tagged EFI_MEMORY_RUNTIME, since those
|
||||
* regions have now been freed.
|
||||
*/
|
||||
new_md = new;
|
||||
for_each_efi_memory_desc(md) {
|
||||
if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
|
||||
(md->type == EFI_BOOT_SERVICES_CODE ||
|
||||
md->type == EFI_BOOT_SERVICES_DATA))
|
||||
continue;
|
||||
|
||||
memcpy(new_md, md, efi.memmap.desc_size);
|
||||
new_md += efi.memmap.desc_size;
|
||||
}
|
||||
|
||||
memunmap(new);
|
||||
|
||||
if (efi_memmap_install(new_phys, num_entries)) {
|
||||
pr_err("Could not install new EFI memmap\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -365,7 +462,7 @@ void __init efi_apply_memmap_quirks(void)
|
|||
*/
|
||||
if (!efi_runtime_supported()) {
|
||||
pr_info("Setup done, disabling due to 32/64-bit mismatch\n");
|
||||
efi_unmap_memmap();
|
||||
efi_memmap_unmap();
|
||||
}
|
||||
|
||||
/* UV2+ BIOS has a fix for this issue. UV1 still needs the quirk. */
|
||||
|
|
|
@ -112,6 +112,23 @@ config EFI_CAPSULE_LOADER
|
|||
|
||||
Most users should say N.
|
||||
|
||||
config EFI_TEST
|
||||
tristate "EFI Runtime Service Tests Support"
|
||||
depends on EFI
|
||||
default n
|
||||
help
|
||||
This driver uses the efi.<service> function pointers directly instead
|
||||
of going through the efivar API, because it is not trying to test the
|
||||
kernel subsystem, just for testing the UEFI runtime service
|
||||
interfaces which are provided by the firmware. This driver is used
|
||||
by the Firmware Test Suite (FWTS) for testing the UEFI runtime
|
||||
interfaces readiness of the firmware.
|
||||
Details for FWTS are available from:
|
||||
<https://wiki.ubuntu.com/FirmwareTestSuite>
|
||||
|
||||
Say Y here to enable the runtime services support via /dev/efi_test.
|
||||
If unsure, say N.
|
||||
|
||||
endmenu
|
||||
|
||||
config UEFI_CPER
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
KASAN_SANITIZE_runtime-wrappers.o := n
|
||||
|
||||
obj-$(CONFIG_EFI) += efi.o vars.o reboot.o memattr.o
|
||||
obj-$(CONFIG_EFI) += capsule.o
|
||||
obj-$(CONFIG_EFI) += capsule.o memmap.o
|
||||
obj-$(CONFIG_EFI_VARS) += efivars.o
|
||||
obj-$(CONFIG_EFI_ESRT) += esrt.o
|
||||
obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o
|
||||
|
@ -20,6 +20,7 @@ obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o
|
|||
obj-$(CONFIG_EFI_STUB) += libstub/
|
||||
obj-$(CONFIG_EFI_FAKE_MEMMAP) += fake_mem.o
|
||||
obj-$(CONFIG_EFI_BOOTLOADER_CONTROL) += efibc.o
|
||||
obj-$(CONFIG_EFI_TEST) += test/
|
||||
|
||||
arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o
|
||||
obj-$(CONFIG_ARM) += $(arm-obj-y)
|
||||
|
|
|
@ -26,9 +26,9 @@
|
|||
|
||||
u64 efi_system_table;
|
||||
|
||||
static int __init is_normal_ram(efi_memory_desc_t *md)
|
||||
static int __init is_memory(efi_memory_desc_t *md)
|
||||
{
|
||||
if (md->attribute & EFI_MEMORY_WB)
|
||||
if (md->attribute & (EFI_MEMORY_WB|EFI_MEMORY_WT|EFI_MEMORY_WC))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
@ -152,9 +152,9 @@ out:
|
|||
}
|
||||
|
||||
/*
|
||||
* Return true for RAM regions we want to permanently reserve.
|
||||
* Return true for regions that can be used as System RAM.
|
||||
*/
|
||||
static __init int is_reserve_region(efi_memory_desc_t *md)
|
||||
static __init int is_usable_memory(efi_memory_desc_t *md)
|
||||
{
|
||||
switch (md->type) {
|
||||
case EFI_LOADER_CODE:
|
||||
|
@ -163,18 +163,22 @@ static __init int is_reserve_region(efi_memory_desc_t *md)
|
|||
case EFI_BOOT_SERVICES_DATA:
|
||||
case EFI_CONVENTIONAL_MEMORY:
|
||||
case EFI_PERSISTENT_MEMORY:
|
||||
return 0;
|
||||
/*
|
||||
* According to the spec, these regions are no longer reserved
|
||||
* after calling ExitBootServices(). However, we can only use
|
||||
* them as System RAM if they can be mapped writeback cacheable.
|
||||
*/
|
||||
return (md->attribute & EFI_MEMORY_WB);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return is_normal_ram(md);
|
||||
return false;
|
||||
}
|
||||
|
||||
static __init void reserve_regions(void)
|
||||
{
|
||||
efi_memory_desc_t *md;
|
||||
u64 paddr, npages, size;
|
||||
int resv;
|
||||
|
||||
if (efi_enabled(EFI_DBG))
|
||||
pr_info("Processing EFI memory map:\n");
|
||||
|
@ -191,32 +195,29 @@ static __init void reserve_regions(void)
|
|||
paddr = md->phys_addr;
|
||||
npages = md->num_pages;
|
||||
|
||||
resv = is_reserve_region(md);
|
||||
if (efi_enabled(EFI_DBG)) {
|
||||
char buf[64];
|
||||
|
||||
pr_info(" 0x%012llx-0x%012llx %s%s\n",
|
||||
pr_info(" 0x%012llx-0x%012llx %s\n",
|
||||
paddr, paddr + (npages << EFI_PAGE_SHIFT) - 1,
|
||||
efi_md_typeattr_format(buf, sizeof(buf), md),
|
||||
resv ? "*" : "");
|
||||
efi_md_typeattr_format(buf, sizeof(buf), md));
|
||||
}
|
||||
|
||||
memrange_efi_to_native(&paddr, &npages);
|
||||
size = npages << PAGE_SHIFT;
|
||||
|
||||
if (is_normal_ram(md))
|
||||
if (is_memory(md)) {
|
||||
early_init_dt_add_memory_arch(paddr, size);
|
||||
|
||||
if (resv)
|
||||
memblock_mark_nomap(paddr, size);
|
||||
|
||||
if (!is_usable_memory(md))
|
||||
memblock_mark_nomap(paddr, size);
|
||||
}
|
||||
}
|
||||
|
||||
set_bit(EFI_MEMMAP, &efi.flags);
|
||||
}
|
||||
|
||||
void __init efi_init(void)
|
||||
{
|
||||
struct efi_memory_map_data data;
|
||||
struct efi_fdt_params params;
|
||||
|
||||
/* Grab UEFI information placed in FDT by stub */
|
||||
|
@ -225,9 +226,12 @@ void __init efi_init(void)
|
|||
|
||||
efi_system_table = params.system_table;
|
||||
|
||||
efi.memmap.phys_map = params.mmap;
|
||||
efi.memmap.map = early_memremap_ro(params.mmap, params.mmap_size);
|
||||
if (efi.memmap.map == NULL) {
|
||||
data.desc_version = params.desc_ver;
|
||||
data.desc_size = params.desc_size;
|
||||
data.size = params.mmap_size;
|
||||
data.phys_map = params.mmap;
|
||||
|
||||
if (efi_memmap_init_early(&data) < 0) {
|
||||
/*
|
||||
* If we are booting via UEFI, the UEFI memory map is the only
|
||||
* description of memory we have, so there is little point in
|
||||
|
@ -235,9 +239,6 @@ void __init efi_init(void)
|
|||
*/
|
||||
panic("Unable to map EFI memory map.\n");
|
||||
}
|
||||
efi.memmap.map_end = efi.memmap.map + params.mmap_size;
|
||||
efi.memmap.desc_size = params.desc_size;
|
||||
efi.memmap.desc_version = params.desc_ver;
|
||||
|
||||
WARN(efi.memmap.desc_version != 1,
|
||||
"Unexpected EFI_MEMORY_DESCRIPTOR version %ld",
|
||||
|
@ -248,7 +249,8 @@ void __init efi_init(void)
|
|||
|
||||
reserve_regions();
|
||||
efi_memattr_init();
|
||||
early_memunmap(efi.memmap.map, params.mmap_size);
|
||||
efi_esrt_init();
|
||||
efi_memmap_unmap();
|
||||
|
||||
memblock_reserve(params.mmap & PAGE_MASK,
|
||||
PAGE_ALIGN(params.mmap_size +
|
||||
|
|
|
@ -39,6 +39,26 @@ static struct mm_struct efi_mm = {
|
|||
.mmlist = LIST_HEAD_INIT(efi_mm.mmlist),
|
||||
};
|
||||
|
||||
#ifdef CONFIG_ARM64_PTDUMP
|
||||
#include <asm/ptdump.h>
|
||||
|
||||
static struct ptdump_info efi_ptdump_info = {
|
||||
.mm = &efi_mm,
|
||||
.markers = (struct addr_marker[]){
|
||||
{ 0, "UEFI runtime start" },
|
||||
{ TASK_SIZE_64, "UEFI runtime end" }
|
||||
},
|
||||
.base_addr = 0,
|
||||
};
|
||||
|
||||
static int __init ptdump_init(void)
|
||||
{
|
||||
return ptdump_register(&efi_ptdump_info, "efi_page_tables");
|
||||
}
|
||||
device_initcall(ptdump_init);
|
||||
|
||||
#endif
|
||||
|
||||
static bool __init efi_virtmap_init(void)
|
||||
{
|
||||
efi_memory_desc_t *md;
|
||||
|
@ -114,14 +134,12 @@ static int __init arm_enable_runtime_services(void)
|
|||
|
||||
pr_info("Remapping and enabling EFI services.\n");
|
||||
|
||||
mapsize = efi.memmap.map_end - efi.memmap.map;
|
||||
mapsize = efi.memmap.desc_size * efi.memmap.nr_map;
|
||||
|
||||
efi.memmap.map = memremap(efi.memmap.phys_map, mapsize, MEMREMAP_WB);
|
||||
if (!efi.memmap.map) {
|
||||
if (efi_memmap_init_late(efi.memmap.phys_map, mapsize)) {
|
||||
pr_err("Failed to remap EFI memory map\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
efi.memmap.map_end = efi.memmap.map + mapsize;
|
||||
|
||||
if (!efi_virtmap_init()) {
|
||||
pr_err("UEFI virtual mapping missing or invalid -- runtime services will not be available\n");
|
||||
|
|
|
@ -125,16 +125,19 @@ static void efi_pstore_scan_sysfs_enter(struct efivar_entry *pos,
|
|||
* @entry: deleting entry
|
||||
* @turn_off_scanning: Check if a scanning flag should be turned off
|
||||
*/
|
||||
static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
|
||||
static inline int __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
|
||||
bool turn_off_scanning)
|
||||
{
|
||||
if (entry->deleting) {
|
||||
list_del(&entry->list);
|
||||
efivar_entry_iter_end();
|
||||
efivar_unregister(entry);
|
||||
efivar_entry_iter_begin();
|
||||
if (efivar_entry_iter_begin())
|
||||
return -EINTR;
|
||||
} else if (turn_off_scanning)
|
||||
entry->scanning = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -144,13 +147,18 @@ static inline void __efi_pstore_scan_sysfs_exit(struct efivar_entry *entry,
|
|||
* @head: list head
|
||||
* @stop: a flag checking if scanning will stop
|
||||
*/
|
||||
static void efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
|
||||
static int efi_pstore_scan_sysfs_exit(struct efivar_entry *pos,
|
||||
struct efivar_entry *next,
|
||||
struct list_head *head, bool stop)
|
||||
{
|
||||
__efi_pstore_scan_sysfs_exit(pos, true);
|
||||
int ret = __efi_pstore_scan_sysfs_exit(pos, true);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (stop)
|
||||
__efi_pstore_scan_sysfs_exit(next, &next->list != head);
|
||||
ret = __efi_pstore_scan_sysfs_exit(next, &next->list != head);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -172,13 +180,17 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
|
|||
struct efivar_entry *entry, *n;
|
||||
struct list_head *head = &efivar_sysfs_list;
|
||||
int size = 0;
|
||||
int ret;
|
||||
|
||||
if (!*pos) {
|
||||
list_for_each_entry_safe(entry, n, head, list) {
|
||||
efi_pstore_scan_sysfs_enter(entry, n, head);
|
||||
|
||||
size = efi_pstore_read_func(entry, data);
|
||||
efi_pstore_scan_sysfs_exit(entry, n, head, size < 0);
|
||||
ret = efi_pstore_scan_sysfs_exit(entry, n, head,
|
||||
size < 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (size)
|
||||
break;
|
||||
}
|
||||
|
@ -190,7 +202,9 @@ static int efi_pstore_sysfs_entry_iter(void *data, struct efivar_entry **pos)
|
|||
efi_pstore_scan_sysfs_enter((*pos), n, head);
|
||||
|
||||
size = efi_pstore_read_func((*pos), data);
|
||||
efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
|
||||
ret = efi_pstore_scan_sysfs_exit((*pos), n, head, size < 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (size)
|
||||
break;
|
||||
}
|
||||
|
@ -232,7 +246,10 @@ static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
|
|||
if (!*data.buf)
|
||||
return -ENOMEM;
|
||||
|
||||
efivar_entry_iter_begin();
|
||||
if (efivar_entry_iter_begin()) {
|
||||
kfree(*data.buf);
|
||||
return -EINTR;
|
||||
}
|
||||
size = efi_pstore_sysfs_entry_iter(&data,
|
||||
(struct efivar_entry **)&psi->data);
|
||||
efivar_entry_iter_end();
|
||||
|
@ -347,7 +364,8 @@ static int efi_pstore_erase(enum pstore_type_id type, u64 id, int count,
|
|||
edata.time = time;
|
||||
edata.name = efi_name;
|
||||
|
||||
efivar_entry_iter_begin();
|
||||
if (efivar_entry_iter_begin())
|
||||
return -EINTR;
|
||||
found = __efivar_entry_iter(efi_pstore_erase_func, &efivar_sysfs_list, &edata, &entry);
|
||||
|
||||
if (found && !entry->scanning) {
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/ucs2_string.h>
|
||||
#include <linux/memblock.h>
|
||||
|
||||
#include <asm/early_ioremap.h>
|
||||
|
||||
|
@ -347,56 +348,31 @@ subsys_initcall(efisubsys_init);
|
|||
|
||||
/*
|
||||
* Find the efi memory descriptor for a given physical address. Given a
|
||||
* physicall address, determine if it exists within an EFI Memory Map entry,
|
||||
* physical address, determine if it exists within an EFI Memory Map entry,
|
||||
* and if so, populate the supplied memory descriptor with the appropriate
|
||||
* data.
|
||||
*/
|
||||
int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
|
||||
{
|
||||
struct efi_memory_map *map = &efi.memmap;
|
||||
phys_addr_t p, e;
|
||||
efi_memory_desc_t *md;
|
||||
|
||||
if (!efi_enabled(EFI_MEMMAP)) {
|
||||
pr_err_once("EFI_MEMMAP is not enabled.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!map) {
|
||||
pr_err_once("efi.memmap is not set.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!out_md) {
|
||||
pr_err_once("out_md is null.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (WARN_ON_ONCE(!map->phys_map))
|
||||
return -EINVAL;
|
||||
if (WARN_ON_ONCE(map->nr_map == 0) || WARN_ON_ONCE(map->desc_size == 0))
|
||||
return -EINVAL;
|
||||
|
||||
e = map->phys_map + map->nr_map * map->desc_size;
|
||||
for (p = map->phys_map; p < e; p += map->desc_size) {
|
||||
efi_memory_desc_t *md;
|
||||
for_each_efi_memory_desc(md) {
|
||||
u64 size;
|
||||
u64 end;
|
||||
|
||||
/*
|
||||
* If a driver calls this after efi_free_boot_services,
|
||||
* ->map will be NULL, and the target may also not be mapped.
|
||||
* So just always get our own virtual map on the CPU.
|
||||
*
|
||||
*/
|
||||
md = early_memremap(p, sizeof (*md));
|
||||
if (!md) {
|
||||
pr_err_once("early_memremap(%pa, %zu) failed.\n",
|
||||
&p, sizeof (*md));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
|
||||
md->type != EFI_BOOT_SERVICES_DATA &&
|
||||
md->type != EFI_RUNTIME_SERVICES_DATA) {
|
||||
early_memunmap(md, sizeof (*md));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -404,11 +380,8 @@ int __init efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md)
|
|||
end = md->phys_addr + size;
|
||||
if (phys_addr >= md->phys_addr && phys_addr < end) {
|
||||
memcpy(out_md, md, sizeof(*out_md));
|
||||
early_memunmap(md, sizeof (*md));
|
||||
return 0;
|
||||
}
|
||||
|
||||
early_memunmap(md, sizeof (*md));
|
||||
}
|
||||
pr_err_once("requested map not found.\n");
|
||||
return -ENOENT;
|
||||
|
@ -424,6 +397,35 @@ u64 __init efi_mem_desc_end(efi_memory_desc_t *md)
|
|||
return end;
|
||||
}
|
||||
|
||||
void __init __weak efi_arch_mem_reserve(phys_addr_t addr, u64 size) {}
|
||||
|
||||
/**
|
||||
* efi_mem_reserve - Reserve an EFI memory region
|
||||
* @addr: Physical address to reserve
|
||||
* @size: Size of reservation
|
||||
*
|
||||
* Mark a region as reserved from general kernel allocation and
|
||||
* prevent it being released by efi_free_boot_services().
|
||||
*
|
||||
* This function should be called drivers once they've parsed EFI
|
||||
* configuration tables to figure out where their data lives, e.g.
|
||||
* efi_esrt_init().
|
||||
*/
|
||||
void __init efi_mem_reserve(phys_addr_t addr, u64 size)
|
||||
{
|
||||
if (!memblock_is_region_reserved(addr, size))
|
||||
memblock_reserve(addr, size);
|
||||
|
||||
/*
|
||||
* Some architectures (x86) reserve all boot services ranges
|
||||
* until efi_free_boot_services() because of buggy firmware
|
||||
* implementations. This means the above memblock_reserve() is
|
||||
* superfluous on x86 and instead what it needs to do is
|
||||
* ensure the @start, @size is not freed.
|
||||
*/
|
||||
efi_arch_mem_reserve(addr, size);
|
||||
}
|
||||
|
||||
static __initdata efi_config_table_type_t common_tables[] = {
|
||||
{ACPI_20_TABLE_GUID, "ACPI 2.0", &efi.acpi20},
|
||||
{ACPI_TABLE_GUID, "ACPI", &efi.acpi},
|
||||
|
@ -811,6 +813,9 @@ int efi_status_to_err(efi_status_t status)
|
|||
case EFI_NOT_FOUND:
|
||||
err = -ENOENT;
|
||||
break;
|
||||
case EFI_ABORTED:
|
||||
err = -EINTR;
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
|
|
@ -510,7 +510,8 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
|
|||
vendor = del_var->VendorGuid;
|
||||
}
|
||||
|
||||
efivar_entry_iter_begin();
|
||||
if (efivar_entry_iter_begin())
|
||||
return -EINTR;
|
||||
entry = efivar_entry_find(name, vendor, &efivar_sysfs_list, true);
|
||||
if (!entry)
|
||||
err = -EINVAL;
|
||||
|
@ -575,7 +576,10 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)
|
|||
return ret;
|
||||
|
||||
kobject_uevent(&new_var->kobj, KOBJ_ADD);
|
||||
efivar_entry_add(new_var, &efivar_sysfs_list);
|
||||
if (efivar_entry_add(new_var, &efivar_sysfs_list)) {
|
||||
efivar_unregister(new_var);
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -690,7 +694,10 @@ static int efivars_sysfs_callback(efi_char16_t *name, efi_guid_t vendor,
|
|||
|
||||
static int efivar_sysfs_destroy(struct efivar_entry *entry, void *data)
|
||||
{
|
||||
efivar_entry_remove(entry);
|
||||
int err = efivar_entry_remove(entry);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
efivar_unregister(entry);
|
||||
return 0;
|
||||
}
|
||||
|
@ -698,7 +705,14 @@ static int efivar_sysfs_destroy(struct efivar_entry *entry, void *data)
|
|||
static void efivars_sysfs_exit(void)
|
||||
{
|
||||
/* Remove all entries and destroy */
|
||||
__efivar_entry_iter(efivar_sysfs_destroy, &efivar_sysfs_list, NULL, NULL);
|
||||
int err;
|
||||
|
||||
err = __efivar_entry_iter(efivar_sysfs_destroy, &efivar_sysfs_list,
|
||||
NULL, NULL);
|
||||
if (err) {
|
||||
pr_err("efivars: Failed to destroy sysfs entries\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (efivars_new_var)
|
||||
sysfs_remove_bin_file(&efivars_kset->kobj, efivars_new_var);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/device.h>
|
||||
#include <linux/efi.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/list.h>
|
||||
|
@ -235,7 +236,7 @@ static struct attribute_group esrt_attr_group = {
|
|||
};
|
||||
|
||||
/*
|
||||
* remap the table, copy it to kmalloced pages, and unmap it.
|
||||
* remap the table, validate it, mark it reserved and unmap it.
|
||||
*/
|
||||
void __init efi_esrt_init(void)
|
||||
{
|
||||
|
@ -335,7 +336,7 @@ void __init efi_esrt_init(void)
|
|||
|
||||
end = esrt_data + size;
|
||||
pr_info("Reserving ESRT space from %pa to %pa.\n", &esrt_data, &end);
|
||||
memblock_reserve(esrt_data, esrt_data_size);
|
||||
efi_mem_reserve(esrt_data, esrt_data_size);
|
||||
|
||||
pr_debug("esrt-init: loaded.\n");
|
||||
err_memunmap:
|
||||
|
@ -382,28 +383,18 @@ static void cleanup_entry_list(void)
|
|||
static int __init esrt_sysfs_init(void)
|
||||
{
|
||||
int error;
|
||||
struct efi_system_resource_table __iomem *ioesrt;
|
||||
|
||||
pr_debug("esrt-sysfs: loading.\n");
|
||||
if (!esrt_data || !esrt_data_size)
|
||||
return -ENOSYS;
|
||||
|
||||
ioesrt = ioremap(esrt_data, esrt_data_size);
|
||||
if (!ioesrt) {
|
||||
pr_err("ioremap(%pa, %zu) failed.\n", &esrt_data,
|
||||
esrt = memremap(esrt_data, esrt_data_size, MEMREMAP_WB);
|
||||
if (!esrt) {
|
||||
pr_err("memremap(%pa, %zu) failed.\n", &esrt_data,
|
||||
esrt_data_size);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
esrt = kmalloc(esrt_data_size, GFP_KERNEL);
|
||||
if (!esrt) {
|
||||
pr_err("kmalloc failed. (wanted %zu bytes)\n", esrt_data_size);
|
||||
iounmap(ioesrt);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy_fromio(esrt, ioesrt, esrt_data_size);
|
||||
|
||||
esrt_kobj = kobject_create_and_add("esrt", efi_kobj);
|
||||
if (!esrt_kobj) {
|
||||
pr_err("Firmware table registration failed.\n");
|
||||
|
@ -429,8 +420,6 @@ static int __init esrt_sysfs_init(void)
|
|||
if (error)
|
||||
goto err_cleanup_list;
|
||||
|
||||
memblock_remove(esrt_data, esrt_data_size);
|
||||
|
||||
pr_debug("esrt-sysfs: loaded.\n");
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -35,17 +35,13 @@
|
|||
|
||||
#define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM
|
||||
|
||||
struct fake_mem {
|
||||
struct range range;
|
||||
u64 attribute;
|
||||
};
|
||||
static struct fake_mem fake_mems[EFI_MAX_FAKEMEM];
|
||||
static struct efi_mem_range fake_mems[EFI_MAX_FAKEMEM];
|
||||
static int nr_fake_mem;
|
||||
|
||||
static int __init cmp_fake_mem(const void *x1, const void *x2)
|
||||
{
|
||||
const struct fake_mem *m1 = x1;
|
||||
const struct fake_mem *m2 = x2;
|
||||
const struct efi_mem_range *m1 = x1;
|
||||
const struct efi_mem_range *m2 = x2;
|
||||
|
||||
if (m1->range.start < m2->range.start)
|
||||
return -1;
|
||||
|
@ -56,40 +52,21 @@ static int __init cmp_fake_mem(const void *x1, const void *x2)
|
|||
|
||||
void __init efi_fake_memmap(void)
|
||||
{
|
||||
u64 start, end, m_start, m_end, m_attr;
|
||||
int new_nr_map = efi.memmap.nr_map;
|
||||
efi_memory_desc_t *md;
|
||||
phys_addr_t new_memmap_phy;
|
||||
void *new_memmap;
|
||||
void *old, *new;
|
||||
int i;
|
||||
|
||||
if (!nr_fake_mem || !efi_enabled(EFI_MEMMAP))
|
||||
if (!nr_fake_mem)
|
||||
return;
|
||||
|
||||
/* count up the number of EFI memory descriptor */
|
||||
for_each_efi_memory_desc(md) {
|
||||
start = md->phys_addr;
|
||||
end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1;
|
||||
for (i = 0; i < nr_fake_mem; i++) {
|
||||
for_each_efi_memory_desc(md) {
|
||||
struct range *r = &fake_mems[i].range;
|
||||
|
||||
for (i = 0; i < nr_fake_mem; i++) {
|
||||
/* modifying range */
|
||||
m_start = fake_mems[i].range.start;
|
||||
m_end = fake_mems[i].range.end;
|
||||
|
||||
if (m_start <= start) {
|
||||
/* split into 2 parts */
|
||||
if (start < m_end && m_end < end)
|
||||
new_nr_map++;
|
||||
}
|
||||
if (start < m_start && m_start < end) {
|
||||
/* split into 3 parts */
|
||||
if (m_end < end)
|
||||
new_nr_map += 2;
|
||||
/* split into 2 parts */
|
||||
if (end <= m_end)
|
||||
new_nr_map++;
|
||||
}
|
||||
new_nr_map += efi_memmap_split_count(md, r);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,85 +84,13 @@ void __init efi_fake_memmap(void)
|
|||
return;
|
||||
}
|
||||
|
||||
for (old = efi.memmap.map, new = new_memmap;
|
||||
old < efi.memmap.map_end;
|
||||
old += efi.memmap.desc_size, new += efi.memmap.desc_size) {
|
||||
|
||||
/* copy original EFI memory descriptor */
|
||||
memcpy(new, old, efi.memmap.desc_size);
|
||||
md = new;
|
||||
start = md->phys_addr;
|
||||
end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1;
|
||||
|
||||
for (i = 0; i < nr_fake_mem; i++) {
|
||||
/* modifying range */
|
||||
m_start = fake_mems[i].range.start;
|
||||
m_end = fake_mems[i].range.end;
|
||||
m_attr = fake_mems[i].attribute;
|
||||
|
||||
if (m_start <= start && end <= m_end)
|
||||
md->attribute |= m_attr;
|
||||
|
||||
if (m_start <= start &&
|
||||
(start < m_end && m_end < end)) {
|
||||
/* first part */
|
||||
md->attribute |= m_attr;
|
||||
md->num_pages = (m_end - md->phys_addr + 1) >>
|
||||
EFI_PAGE_SHIFT;
|
||||
/* latter part */
|
||||
new += efi.memmap.desc_size;
|
||||
memcpy(new, old, efi.memmap.desc_size);
|
||||
md = new;
|
||||
md->phys_addr = m_end + 1;
|
||||
md->num_pages = (end - md->phys_addr + 1) >>
|
||||
EFI_PAGE_SHIFT;
|
||||
}
|
||||
|
||||
if ((start < m_start && m_start < end) && m_end < end) {
|
||||
/* first part */
|
||||
md->num_pages = (m_start - md->phys_addr) >>
|
||||
EFI_PAGE_SHIFT;
|
||||
/* middle part */
|
||||
new += efi.memmap.desc_size;
|
||||
memcpy(new, old, efi.memmap.desc_size);
|
||||
md = new;
|
||||
md->attribute |= m_attr;
|
||||
md->phys_addr = m_start;
|
||||
md->num_pages = (m_end - m_start + 1) >>
|
||||
EFI_PAGE_SHIFT;
|
||||
/* last part */
|
||||
new += efi.memmap.desc_size;
|
||||
memcpy(new, old, efi.memmap.desc_size);
|
||||
md = new;
|
||||
md->phys_addr = m_end + 1;
|
||||
md->num_pages = (end - m_end) >>
|
||||
EFI_PAGE_SHIFT;
|
||||
}
|
||||
|
||||
if ((start < m_start && m_start < end) &&
|
||||
(end <= m_end)) {
|
||||
/* first part */
|
||||
md->num_pages = (m_start - md->phys_addr) >>
|
||||
EFI_PAGE_SHIFT;
|
||||
/* latter part */
|
||||
new += efi.memmap.desc_size;
|
||||
memcpy(new, old, efi.memmap.desc_size);
|
||||
md = new;
|
||||
md->phys_addr = m_start;
|
||||
md->num_pages = (end - md->phys_addr + 1) >>
|
||||
EFI_PAGE_SHIFT;
|
||||
md->attribute |= m_attr;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < nr_fake_mem; i++)
|
||||
efi_memmap_insert(&efi.memmap, new_memmap, &fake_mems[i]);
|
||||
|
||||
/* swap into new EFI memmap */
|
||||
efi_unmap_memmap();
|
||||
efi.memmap.map = new_memmap;
|
||||
efi.memmap.phys_map = new_memmap_phy;
|
||||
efi.memmap.nr_map = new_nr_map;
|
||||
efi.memmap.map_end = efi.memmap.map + efi.memmap.nr_map * efi.memmap.desc_size;
|
||||
set_bit(EFI_MEMMAP, &efi.flags);
|
||||
early_memunmap(new_memmap, efi.memmap.desc_size * new_nr_map);
|
||||
|
||||
efi_memmap_install(new_memmap_phy, new_nr_map);
|
||||
|
||||
/* print new EFI memmap */
|
||||
efi_print_memmap();
|
||||
|
@ -223,7 +128,7 @@ static int __init setup_fake_mem(char *p)
|
|||
p++;
|
||||
}
|
||||
|
||||
sort(fake_mems, nr_fake_mem, sizeof(struct fake_mem),
|
||||
sort(fake_mems, nr_fake_mem, sizeof(struct efi_mem_range),
|
||||
cmp_fake_mem, NULL);
|
||||
|
||||
for (i = 0; i < nr_fake_mem; i++)
|
||||
|
|
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
* Common EFI memory map functions.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "efi: " fmt
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/efi.h>
|
||||
#include <linux/io.h>
|
||||
#include <asm/early_ioremap.h>
|
||||
|
||||
/**
|
||||
* __efi_memmap_init - Common code for mapping the EFI memory map
|
||||
* @data: EFI memory map data
|
||||
* @late: Use early or late mapping function?
|
||||
*
|
||||
* This function takes care of figuring out which function to use to
|
||||
* map the EFI memory map in efi.memmap based on how far into the boot
|
||||
* we are.
|
||||
*
|
||||
* During bootup @late should be %false since we only have access to
|
||||
* the early_memremap*() functions as the vmalloc space isn't setup.
|
||||
* Once the kernel is fully booted we can fallback to the more robust
|
||||
* memremap*() API.
|
||||
*
|
||||
* Returns zero on success, a negative error code on failure.
|
||||
*/
|
||||
static int __init
|
||||
__efi_memmap_init(struct efi_memory_map_data *data, bool late)
|
||||
{
|
||||
struct efi_memory_map map;
|
||||
phys_addr_t phys_map;
|
||||
|
||||
if (efi_enabled(EFI_PARAVIRT))
|
||||
return 0;
|
||||
|
||||
phys_map = data->phys_map;
|
||||
|
||||
if (late)
|
||||
map.map = memremap(phys_map, data->size, MEMREMAP_WB);
|
||||
else
|
||||
map.map = early_memremap(phys_map, data->size);
|
||||
|
||||
if (!map.map) {
|
||||
pr_err("Could not map the memory map!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
map.phys_map = data->phys_map;
|
||||
map.nr_map = data->size / data->desc_size;
|
||||
map.map_end = map.map + data->size;
|
||||
|
||||
map.desc_version = data->desc_version;
|
||||
map.desc_size = data->desc_size;
|
||||
map.late = late;
|
||||
|
||||
set_bit(EFI_MEMMAP, &efi.flags);
|
||||
|
||||
efi.memmap = map;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_memmap_init_early - Map the EFI memory map data structure
|
||||
* @data: EFI memory map data
|
||||
*
|
||||
* Use early_memremap() to map the passed in EFI memory map and assign
|
||||
* it to efi.memmap.
|
||||
*/
|
||||
int __init efi_memmap_init_early(struct efi_memory_map_data *data)
|
||||
{
|
||||
/* Cannot go backwards */
|
||||
WARN_ON(efi.memmap.late);
|
||||
|
||||
return __efi_memmap_init(data, false);
|
||||
}
|
||||
|
||||
void __init efi_memmap_unmap(void)
|
||||
{
|
||||
if (!efi.memmap.late) {
|
||||
unsigned long size;
|
||||
|
||||
size = efi.memmap.desc_size * efi.memmap.nr_map;
|
||||
early_memunmap(efi.memmap.map, size);
|
||||
} else {
|
||||
memunmap(efi.memmap.map);
|
||||
}
|
||||
|
||||
efi.memmap.map = NULL;
|
||||
clear_bit(EFI_MEMMAP, &efi.flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_memmap_init_late - Map efi.memmap with memremap()
|
||||
* @phys_addr: Physical address of the new EFI memory map
|
||||
* @size: Size in bytes of the new EFI memory map
|
||||
*
|
||||
* Setup a mapping of the EFI memory map using ioremap_cache(). This
|
||||
* function should only be called once the vmalloc space has been
|
||||
* setup and is therefore not suitable for calling during early EFI
|
||||
* initialise, e.g. in efi_init(). Additionally, it expects
|
||||
* efi_memmap_init_early() to have already been called.
|
||||
*
|
||||
* The reason there are two EFI memmap initialisation
|
||||
* (efi_memmap_init_early() and this late version) is because the
|
||||
* early EFI memmap should be explicitly unmapped once EFI
|
||||
* initialisation is complete as the fixmap space used to map the EFI
|
||||
* memmap (via early_memremap()) is a scarce resource.
|
||||
*
|
||||
* This late mapping is intended to persist for the duration of
|
||||
* runtime so that things like efi_mem_desc_lookup() and
|
||||
* efi_mem_attributes() always work.
|
||||
*
|
||||
* Returns zero on success, a negative error code on failure.
|
||||
*/
|
||||
int __init efi_memmap_init_late(phys_addr_t addr, unsigned long size)
|
||||
{
|
||||
struct efi_memory_map_data data = {
|
||||
.phys_map = addr,
|
||||
.size = size,
|
||||
};
|
||||
|
||||
/* Did we forget to unmap the early EFI memmap? */
|
||||
WARN_ON(efi.memmap.map);
|
||||
|
||||
/* Were we already called? */
|
||||
WARN_ON(efi.memmap.late);
|
||||
|
||||
/*
|
||||
* It makes no sense to allow callers to register different
|
||||
* values for the following fields. Copy them out of the
|
||||
* existing early EFI memmap.
|
||||
*/
|
||||
data.desc_version = efi.memmap.desc_version;
|
||||
data.desc_size = efi.memmap.desc_size;
|
||||
|
||||
return __efi_memmap_init(&data, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_memmap_install - Install a new EFI memory map in efi.memmap
|
||||
* @addr: Physical address of the memory map
|
||||
* @nr_map: Number of entries in the memory map
|
||||
*
|
||||
* Unlike efi_memmap_init_*(), this function does not allow the caller
|
||||
* to switch from early to late mappings. It simply uses the existing
|
||||
* mapping function and installs the new memmap.
|
||||
*
|
||||
* Returns zero on success, a negative error code on failure.
|
||||
*/
|
||||
int __init efi_memmap_install(phys_addr_t addr, unsigned int nr_map)
|
||||
{
|
||||
struct efi_memory_map_data data;
|
||||
|
||||
efi_memmap_unmap();
|
||||
|
||||
data.phys_map = addr;
|
||||
data.size = efi.memmap.desc_size * nr_map;
|
||||
data.desc_version = efi.memmap.desc_version;
|
||||
data.desc_size = efi.memmap.desc_size;
|
||||
|
||||
return __efi_memmap_init(&data, efi.memmap.late);
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_memmap_split_count - Count number of additional EFI memmap entries
|
||||
* @md: EFI memory descriptor to split
|
||||
* @range: Address range (start, end) to split around
|
||||
*
|
||||
* Returns the number of additional EFI memmap entries required to
|
||||
* accomodate @range.
|
||||
*/
|
||||
int __init efi_memmap_split_count(efi_memory_desc_t *md, struct range *range)
|
||||
{
|
||||
u64 m_start, m_end;
|
||||
u64 start, end;
|
||||
int count = 0;
|
||||
|
||||
start = md->phys_addr;
|
||||
end = start + (md->num_pages << EFI_PAGE_SHIFT) - 1;
|
||||
|
||||
/* modifying range */
|
||||
m_start = range->start;
|
||||
m_end = range->end;
|
||||
|
||||
if (m_start <= start) {
|
||||
/* split into 2 parts */
|
||||
if (start < m_end && m_end < end)
|
||||
count++;
|
||||
}
|
||||
|
||||
if (start < m_start && m_start < end) {
|
||||
/* split into 3 parts */
|
||||
if (m_end < end)
|
||||
count += 2;
|
||||
/* split into 2 parts */
|
||||
if (end <= m_end)
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* efi_memmap_insert - Insert a memory region in an EFI memmap
|
||||
* @old_memmap: The existing EFI memory map structure
|
||||
* @buf: Address of buffer to store new map
|
||||
* @mem: Memory map entry to insert
|
||||
*
|
||||
* It is suggested that you call efi_memmap_split_count() first
|
||||
* to see how large @buf needs to be.
|
||||
*/
|
||||
void __init efi_memmap_insert(struct efi_memory_map *old_memmap, void *buf,
|
||||
struct efi_mem_range *mem)
|
||||
{
|
||||
u64 m_start, m_end, m_attr;
|
||||
efi_memory_desc_t *md;
|
||||
u64 start, end;
|
||||
void *old, *new;
|
||||
|
||||
/* modifying range */
|
||||
m_start = mem->range.start;
|
||||
m_end = mem->range.end;
|
||||
m_attr = mem->attribute;
|
||||
|
||||
for (old = old_memmap->map, new = buf;
|
||||
old < old_memmap->map_end;
|
||||
old += old_memmap->desc_size, new += old_memmap->desc_size) {
|
||||
|
||||
/* copy original EFI memory descriptor */
|
||||
memcpy(new, old, old_memmap->desc_size);
|
||||
md = new;
|
||||
start = md->phys_addr;
|
||||
end = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1;
|
||||
|
||||
if (m_start <= start && end <= m_end)
|
||||
md->attribute |= m_attr;
|
||||
|
||||
if (m_start <= start &&
|
||||
(start < m_end && m_end < end)) {
|
||||
/* first part */
|
||||
md->attribute |= m_attr;
|
||||
md->num_pages = (m_end - md->phys_addr + 1) >>
|
||||
EFI_PAGE_SHIFT;
|
||||
/* latter part */
|
||||
new += old_memmap->desc_size;
|
||||
memcpy(new, old, old_memmap->desc_size);
|
||||
md = new;
|
||||
md->phys_addr = m_end + 1;
|
||||
md->num_pages = (end - md->phys_addr + 1) >>
|
||||
EFI_PAGE_SHIFT;
|
||||
}
|
||||
|
||||
if ((start < m_start && m_start < end) && m_end < end) {
|
||||
/* first part */
|
||||
md->num_pages = (m_start - md->phys_addr) >>
|
||||
EFI_PAGE_SHIFT;
|
||||
/* middle part */
|
||||
new += old_memmap->desc_size;
|
||||
memcpy(new, old, old_memmap->desc_size);
|
||||
md = new;
|
||||
md->attribute |= m_attr;
|
||||
md->phys_addr = m_start;
|
||||
md->num_pages = (m_end - m_start + 1) >>
|
||||
EFI_PAGE_SHIFT;
|
||||
/* last part */
|
||||
new += old_memmap->desc_size;
|
||||
memcpy(new, old, old_memmap->desc_size);
|
||||
md = new;
|
||||
md->phys_addr = m_end + 1;
|
||||
md->num_pages = (end - m_end) >>
|
||||
EFI_PAGE_SHIFT;
|
||||
}
|
||||
|
||||
if ((start < m_start && m_start < end) &&
|
||||
(end <= m_end)) {
|
||||
/* first part */
|
||||
md->num_pages = (m_start - md->phys_addr) >>
|
||||
EFI_PAGE_SHIFT;
|
||||
/* latter part */
|
||||
new += old_memmap->desc_size;
|
||||
memcpy(new, old, old_memmap->desc_size);
|
||||
md = new;
|
||||
md->phys_addr = m_start;
|
||||
md->num_pages = (end - md->phys_addr + 1) >>
|
||||
EFI_PAGE_SHIFT;
|
||||
md->attribute |= m_attr;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,10 +14,6 @@
|
|||
|
||||
#include <asm/setup.h>
|
||||
|
||||
static void *efi_runtime_map;
|
||||
static int nr_efi_runtime_map;
|
||||
static u32 efi_memdesc_size;
|
||||
|
||||
struct efi_runtime_map_entry {
|
||||
efi_memory_desc_t md;
|
||||
struct kobject kobj; /* kobject for each entry */
|
||||
|
@ -106,7 +102,8 @@ static struct kobj_type __refdata map_ktype = {
|
|||
static struct kset *map_kset;
|
||||
|
||||
static struct efi_runtime_map_entry *
|
||||
add_sysfs_runtime_map_entry(struct kobject *kobj, int nr)
|
||||
add_sysfs_runtime_map_entry(struct kobject *kobj, int nr,
|
||||
efi_memory_desc_t *md)
|
||||
{
|
||||
int ret;
|
||||
struct efi_runtime_map_entry *entry;
|
||||
|
@ -124,8 +121,7 @@ add_sysfs_runtime_map_entry(struct kobject *kobj, int nr)
|
|||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
memcpy(&entry->md, efi_runtime_map + nr * efi_memdesc_size,
|
||||
sizeof(efi_memory_desc_t));
|
||||
memcpy(&entry->md, md, sizeof(efi_memory_desc_t));
|
||||
|
||||
kobject_init(&entry->kobj, &map_ktype);
|
||||
entry->kobj.kset = map_kset;
|
||||
|
@ -142,12 +138,12 @@ add_sysfs_runtime_map_entry(struct kobject *kobj, int nr)
|
|||
|
||||
int efi_get_runtime_map_size(void)
|
||||
{
|
||||
return nr_efi_runtime_map * efi_memdesc_size;
|
||||
return efi.memmap.nr_map * efi.memmap.desc_size;
|
||||
}
|
||||
|
||||
int efi_get_runtime_map_desc_size(void)
|
||||
{
|
||||
return efi_memdesc_size;
|
||||
return efi.memmap.desc_size;
|
||||
}
|
||||
|
||||
int efi_runtime_map_copy(void *buf, size_t bufsz)
|
||||
|
@ -157,38 +153,33 @@ int efi_runtime_map_copy(void *buf, size_t bufsz)
|
|||
if (sz > bufsz)
|
||||
sz = bufsz;
|
||||
|
||||
memcpy(buf, efi_runtime_map, sz);
|
||||
memcpy(buf, efi.memmap.map, sz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void efi_runtime_map_setup(void *map, int nr_entries, u32 desc_size)
|
||||
{
|
||||
efi_runtime_map = map;
|
||||
nr_efi_runtime_map = nr_entries;
|
||||
efi_memdesc_size = desc_size;
|
||||
}
|
||||
|
||||
int __init efi_runtime_map_init(struct kobject *efi_kobj)
|
||||
{
|
||||
int i, j, ret = 0;
|
||||
struct efi_runtime_map_entry *entry;
|
||||
efi_memory_desc_t *md;
|
||||
|
||||
if (!efi_runtime_map)
|
||||
if (!efi_enabled(EFI_MEMMAP))
|
||||
return 0;
|
||||
|
||||
map_entries = kzalloc(nr_efi_runtime_map * sizeof(entry), GFP_KERNEL);
|
||||
map_entries = kzalloc(efi.memmap.nr_map * sizeof(entry), GFP_KERNEL);
|
||||
if (!map_entries) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_efi_runtime_map; i++) {
|
||||
entry = add_sysfs_runtime_map_entry(efi_kobj, i);
|
||||
i = 0;
|
||||
for_each_efi_memory_desc(md) {
|
||||
entry = add_sysfs_runtime_map_entry(efi_kobj, i, md);
|
||||
if (IS_ERR(entry)) {
|
||||
ret = PTR_ERR(entry);
|
||||
goto out_add_entry;
|
||||
}
|
||||
*(map_entries + i) = entry;
|
||||
*(map_entries + i++) = entry;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -14,11 +14,13 @@
|
|||
* This file is released under the GPLv2.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "efi: " fmt
|
||||
|
||||
#include <linux/bug.h>
|
||||
#include <linux/efi.h>
|
||||
#include <linux/irqflags.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/semaphore.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <asm/efi.h>
|
||||
|
||||
|
@ -81,20 +83,21 @@ void efi_call_virt_check_flags(unsigned long flags, const char *call)
|
|||
* +------------------------------------+-------------------------------+
|
||||
*
|
||||
* Due to the fact that the EFI pstore may write to the variable store in
|
||||
* interrupt context, we need to use a spinlock for at least the groups that
|
||||
* interrupt context, we need to use a lock for at least the groups that
|
||||
* contain SetVariable() and QueryVariableInfo(). That leaves little else, as
|
||||
* none of the remaining functions are actually ever called at runtime.
|
||||
* So let's just use a single spinlock to serialize all Runtime Services calls.
|
||||
* So let's just use a single lock to serialize all Runtime Services calls.
|
||||
*/
|
||||
static DEFINE_SPINLOCK(efi_runtime_lock);
|
||||
static DEFINE_SEMAPHORE(efi_runtime_lock);
|
||||
|
||||
static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc)
|
||||
{
|
||||
efi_status_t status;
|
||||
|
||||
spin_lock(&efi_runtime_lock);
|
||||
if (down_interruptible(&efi_runtime_lock))
|
||||
return EFI_ABORTED;
|
||||
status = efi_call_virt(get_time, tm, tc);
|
||||
spin_unlock(&efi_runtime_lock);
|
||||
up(&efi_runtime_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -102,9 +105,10 @@ static efi_status_t virt_efi_set_time(efi_time_t *tm)
|
|||
{
|
||||
efi_status_t status;
|
||||
|
||||
spin_lock(&efi_runtime_lock);
|
||||
if (down_interruptible(&efi_runtime_lock))
|
||||
return EFI_ABORTED;
|
||||
status = efi_call_virt(set_time, tm);
|
||||
spin_unlock(&efi_runtime_lock);
|
||||
up(&efi_runtime_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -114,9 +118,10 @@ static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled,
|
|||
{
|
||||
efi_status_t status;
|
||||
|
||||
spin_lock(&efi_runtime_lock);
|
||||
if (down_interruptible(&efi_runtime_lock))
|
||||
return EFI_ABORTED;
|
||||
status = efi_call_virt(get_wakeup_time, enabled, pending, tm);
|
||||
spin_unlock(&efi_runtime_lock);
|
||||
up(&efi_runtime_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -124,9 +129,10 @@ static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm)
|
|||
{
|
||||
efi_status_t status;
|
||||
|
||||
spin_lock(&efi_runtime_lock);
|
||||
if (down_interruptible(&efi_runtime_lock))
|
||||
return EFI_ABORTED;
|
||||
status = efi_call_virt(set_wakeup_time, enabled, tm);
|
||||
spin_unlock(&efi_runtime_lock);
|
||||
up(&efi_runtime_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -138,10 +144,11 @@ static efi_status_t virt_efi_get_variable(efi_char16_t *name,
|
|||
{
|
||||
efi_status_t status;
|
||||
|
||||
spin_lock(&efi_runtime_lock);
|
||||
if (down_interruptible(&efi_runtime_lock))
|
||||
return EFI_ABORTED;
|
||||
status = efi_call_virt(get_variable, name, vendor, attr, data_size,
|
||||
data);
|
||||
spin_unlock(&efi_runtime_lock);
|
||||
up(&efi_runtime_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -151,9 +158,10 @@ static efi_status_t virt_efi_get_next_variable(unsigned long *name_size,
|
|||
{
|
||||
efi_status_t status;
|
||||
|
||||
spin_lock(&efi_runtime_lock);
|
||||
if (down_interruptible(&efi_runtime_lock))
|
||||
return EFI_ABORTED;
|
||||
status = efi_call_virt(get_next_variable, name_size, name, vendor);
|
||||
spin_unlock(&efi_runtime_lock);
|
||||
up(&efi_runtime_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -165,10 +173,11 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name,
|
|||
{
|
||||
efi_status_t status;
|
||||
|
||||
spin_lock(&efi_runtime_lock);
|
||||
if (down_interruptible(&efi_runtime_lock))
|
||||
return EFI_ABORTED;
|
||||
status = efi_call_virt(set_variable, name, vendor, attr, data_size,
|
||||
data);
|
||||
spin_unlock(&efi_runtime_lock);
|
||||
up(&efi_runtime_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -179,12 +188,12 @@ virt_efi_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor,
|
|||
{
|
||||
efi_status_t status;
|
||||
|
||||
if (!spin_trylock(&efi_runtime_lock))
|
||||
if (down_trylock(&efi_runtime_lock))
|
||||
return EFI_NOT_READY;
|
||||
|
||||
status = efi_call_virt(set_variable, name, vendor, attr, data_size,
|
||||
data);
|
||||
spin_unlock(&efi_runtime_lock);
|
||||
up(&efi_runtime_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -199,10 +208,11 @@ static efi_status_t virt_efi_query_variable_info(u32 attr,
|
|||
if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
spin_lock(&efi_runtime_lock);
|
||||
if (down_interruptible(&efi_runtime_lock))
|
||||
return EFI_ABORTED;
|
||||
status = efi_call_virt(query_variable_info, attr, storage_space,
|
||||
remaining_space, max_variable_size);
|
||||
spin_unlock(&efi_runtime_lock);
|
||||
up(&efi_runtime_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -217,12 +227,12 @@ virt_efi_query_variable_info_nonblocking(u32 attr,
|
|||
if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
if (!spin_trylock(&efi_runtime_lock))
|
||||
if (down_trylock(&efi_runtime_lock))
|
||||
return EFI_NOT_READY;
|
||||
|
||||
status = efi_call_virt(query_variable_info, attr, storage_space,
|
||||
remaining_space, max_variable_size);
|
||||
spin_unlock(&efi_runtime_lock);
|
||||
up(&efi_runtime_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -230,9 +240,10 @@ static efi_status_t virt_efi_get_next_high_mono_count(u32 *count)
|
|||
{
|
||||
efi_status_t status;
|
||||
|
||||
spin_lock(&efi_runtime_lock);
|
||||
if (down_interruptible(&efi_runtime_lock))
|
||||
return EFI_ABORTED;
|
||||
status = efi_call_virt(get_next_high_mono_count, count);
|
||||
spin_unlock(&efi_runtime_lock);
|
||||
up(&efi_runtime_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -241,9 +252,13 @@ static void virt_efi_reset_system(int reset_type,
|
|||
unsigned long data_size,
|
||||
efi_char16_t *data)
|
||||
{
|
||||
spin_lock(&efi_runtime_lock);
|
||||
if (down_interruptible(&efi_runtime_lock)) {
|
||||
pr_warn("failed to invoke the reset_system() runtime service:\n"
|
||||
"could not get exclusive access to the firmware\n");
|
||||
return;
|
||||
}
|
||||
__efi_call_virt(reset_system, reset_type, status, data_size, data);
|
||||
spin_unlock(&efi_runtime_lock);
|
||||
up(&efi_runtime_lock);
|
||||
}
|
||||
|
||||
static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules,
|
||||
|
@ -255,9 +270,10 @@ static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules,
|
|||
if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
spin_lock(&efi_runtime_lock);
|
||||
if (down_interruptible(&efi_runtime_lock))
|
||||
return EFI_ABORTED;
|
||||
status = efi_call_virt(update_capsule, capsules, count, sg_list);
|
||||
spin_unlock(&efi_runtime_lock);
|
||||
up(&efi_runtime_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -271,10 +287,11 @@ static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules,
|
|||
if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
|
||||
return EFI_UNSUPPORTED;
|
||||
|
||||
spin_lock(&efi_runtime_lock);
|
||||
if (down_interruptible(&efi_runtime_lock))
|
||||
return EFI_ABORTED;
|
||||
status = efi_call_virt(query_capsule_caps, capsules, count, max_size,
|
||||
reset_type);
|
||||
spin_unlock(&efi_runtime_lock);
|
||||
up(&efi_runtime_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
obj-$(CONFIG_EFI_TEST) += efi_test.o
|
|
@ -0,0 +1,749 @@
|
|||
/*
|
||||
* EFI Test Driver for Runtime Services
|
||||
*
|
||||
* Copyright(C) 2012-2016 Canonical Ltd.
|
||||
*
|
||||
* This driver exports EFI runtime services interfaces into userspace, which
|
||||
* allow to use and test UEFI runtime services provided by firmware.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/efi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "efi_test.h"
|
||||
|
||||
MODULE_AUTHOR("Ivan Hu <ivan.hu@canonical.com>");
|
||||
MODULE_DESCRIPTION("EFI Test Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/*
|
||||
* Count the bytes in 'str', including the terminating NULL.
|
||||
*
|
||||
* Note this function returns the number of *bytes*, not the number of
|
||||
* ucs2 characters.
|
||||
*/
|
||||
static inline size_t user_ucs2_strsize(efi_char16_t __user *str)
|
||||
{
|
||||
efi_char16_t *s = str, c;
|
||||
size_t len;
|
||||
|
||||
if (!str)
|
||||
return 0;
|
||||
|
||||
/* Include terminating NULL */
|
||||
len = sizeof(efi_char16_t);
|
||||
|
||||
if (get_user(c, s++)) {
|
||||
/* Can't read userspace memory for size */
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (c != 0) {
|
||||
if (get_user(c, s++)) {
|
||||
/* Can't read userspace memory for size */
|
||||
return 0;
|
||||
}
|
||||
len += sizeof(efi_char16_t);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate a buffer and copy a ucs2 string from user space into it.
|
||||
*/
|
||||
static inline int
|
||||
copy_ucs2_from_user_len(efi_char16_t **dst, efi_char16_t __user *src,
|
||||
size_t len)
|
||||
{
|
||||
efi_char16_t *buf;
|
||||
|
||||
if (!src) {
|
||||
*dst = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!access_ok(VERIFY_READ, src, 1))
|
||||
return -EFAULT;
|
||||
|
||||
buf = kmalloc(len, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
*dst = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
*dst = buf;
|
||||
|
||||
if (copy_from_user(*dst, src, len)) {
|
||||
kfree(buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Count the bytes in 'str', including the terminating NULL.
|
||||
*
|
||||
* Just a wrap for user_ucs2_strsize
|
||||
*/
|
||||
static inline int
|
||||
get_ucs2_strsize_from_user(efi_char16_t __user *src, size_t *len)
|
||||
{
|
||||
if (!access_ok(VERIFY_READ, src, 1))
|
||||
return -EFAULT;
|
||||
|
||||
*len = user_ucs2_strsize(src);
|
||||
if (*len == 0)
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the required buffer allocation size and copy a ucs2 string
|
||||
* from user space into it.
|
||||
*
|
||||
* This function differs from copy_ucs2_from_user_len() because it
|
||||
* calculates the size of the buffer to allocate by taking the length of
|
||||
* the string 'src'.
|
||||
*
|
||||
* If a non-zero value is returned, the caller MUST NOT access 'dst'.
|
||||
*
|
||||
* It is the caller's responsibility to free 'dst'.
|
||||
*/
|
||||
static inline int
|
||||
copy_ucs2_from_user(efi_char16_t **dst, efi_char16_t __user *src)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
if (!access_ok(VERIFY_READ, src, 1))
|
||||
return -EFAULT;
|
||||
|
||||
len = user_ucs2_strsize(src);
|
||||
if (len == 0)
|
||||
return -EFAULT;
|
||||
return copy_ucs2_from_user_len(dst, src, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy a ucs2 string to a user buffer.
|
||||
*
|
||||
* This function is a simple wrapper around copy_to_user() that does
|
||||
* nothing if 'src' is NULL, which is useful for reducing the amount of
|
||||
* NULL checking the caller has to do.
|
||||
*
|
||||
* 'len' specifies the number of bytes to copy.
|
||||
*/
|
||||
static inline int
|
||||
copy_ucs2_to_user_len(efi_char16_t __user *dst, efi_char16_t *src, size_t len)
|
||||
{
|
||||
if (!src)
|
||||
return 0;
|
||||
|
||||
if (!access_ok(VERIFY_WRITE, dst, 1))
|
||||
return -EFAULT;
|
||||
|
||||
return copy_to_user(dst, src, len);
|
||||
}
|
||||
|
||||
static long efi_runtime_get_variable(unsigned long arg)
|
||||
{
|
||||
struct efi_getvariable __user *getvariable_user;
|
||||
struct efi_getvariable getvariable;
|
||||
unsigned long datasize, prev_datasize, *dz;
|
||||
efi_guid_t vendor_guid, *vd = NULL;
|
||||
efi_status_t status;
|
||||
efi_char16_t *name = NULL;
|
||||
u32 attr, *at;
|
||||
void *data = NULL;
|
||||
int rv = 0;
|
||||
|
||||
getvariable_user = (struct efi_getvariable __user *)arg;
|
||||
|
||||
if (copy_from_user(&getvariable, getvariable_user,
|
||||
sizeof(getvariable)))
|
||||
return -EFAULT;
|
||||
if (getvariable.data_size &&
|
||||
get_user(datasize, getvariable.data_size))
|
||||
return -EFAULT;
|
||||
if (getvariable.vendor_guid) {
|
||||
if (copy_from_user(&vendor_guid, getvariable.vendor_guid,
|
||||
sizeof(vendor_guid)))
|
||||
return -EFAULT;
|
||||
vd = &vendor_guid;
|
||||
}
|
||||
|
||||
if (getvariable.variable_name) {
|
||||
rv = copy_ucs2_from_user(&name, getvariable.variable_name);
|
||||
if (rv)
|
||||
return rv;
|
||||
}
|
||||
|
||||
at = getvariable.attributes ? &attr : NULL;
|
||||
dz = getvariable.data_size ? &datasize : NULL;
|
||||
|
||||
if (getvariable.data_size && getvariable.data) {
|
||||
data = kmalloc(datasize, GFP_KERNEL);
|
||||
if (!data) {
|
||||
kfree(name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
prev_datasize = datasize;
|
||||
status = efi.get_variable(name, vd, at, dz, data);
|
||||
kfree(name);
|
||||
|
||||
if (put_user(status, getvariable.status)) {
|
||||
rv = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
if (status == EFI_BUFFER_TOO_SMALL) {
|
||||
if (dz && put_user(datasize, getvariable.data_size)) {
|
||||
rv = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
rv = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (prev_datasize < datasize) {
|
||||
rv = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
if (copy_to_user(getvariable.data, data, datasize)) {
|
||||
rv = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (at && put_user(attr, getvariable.attributes)) {
|
||||
rv = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dz && put_user(datasize, getvariable.data_size))
|
||||
rv = -EFAULT;
|
||||
|
||||
out:
|
||||
kfree(data);
|
||||
return rv;
|
||||
|
||||
}
|
||||
|
||||
static long efi_runtime_set_variable(unsigned long arg)
|
||||
{
|
||||
struct efi_setvariable __user *setvariable_user;
|
||||
struct efi_setvariable setvariable;
|
||||
efi_guid_t vendor_guid;
|
||||
efi_status_t status;
|
||||
efi_char16_t *name = NULL;
|
||||
void *data;
|
||||
int rv = 0;
|
||||
|
||||
setvariable_user = (struct efi_setvariable __user *)arg;
|
||||
|
||||
if (copy_from_user(&setvariable, setvariable_user, sizeof(setvariable)))
|
||||
return -EFAULT;
|
||||
if (copy_from_user(&vendor_guid, setvariable.vendor_guid,
|
||||
sizeof(vendor_guid)))
|
||||
return -EFAULT;
|
||||
|
||||
if (setvariable.variable_name) {
|
||||
rv = copy_ucs2_from_user(&name, setvariable.variable_name);
|
||||
if (rv)
|
||||
return rv;
|
||||
}
|
||||
|
||||
data = kmalloc(setvariable.data_size, GFP_KERNEL);
|
||||
if (!data) {
|
||||
kfree(name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (copy_from_user(data, setvariable.data, setvariable.data_size)) {
|
||||
rv = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = efi.set_variable(name, &vendor_guid,
|
||||
setvariable.attributes,
|
||||
setvariable.data_size, data);
|
||||
|
||||
if (put_user(status, setvariable.status)) {
|
||||
rv = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rv = status == EFI_SUCCESS ? 0 : -EINVAL;
|
||||
|
||||
out:
|
||||
kfree(data);
|
||||
kfree(name);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static long efi_runtime_get_time(unsigned long arg)
|
||||
{
|
||||
struct efi_gettime __user *gettime_user;
|
||||
struct efi_gettime gettime;
|
||||
efi_status_t status;
|
||||
efi_time_cap_t cap;
|
||||
efi_time_t efi_time;
|
||||
|
||||
gettime_user = (struct efi_gettime __user *)arg;
|
||||
if (copy_from_user(&gettime, gettime_user, sizeof(gettime)))
|
||||
return -EFAULT;
|
||||
|
||||
status = efi.get_time(gettime.time ? &efi_time : NULL,
|
||||
gettime.capabilities ? &cap : NULL);
|
||||
|
||||
if (put_user(status, gettime.status))
|
||||
return -EFAULT;
|
||||
|
||||
if (status != EFI_SUCCESS)
|
||||
return -EINVAL;
|
||||
|
||||
if (gettime.capabilities) {
|
||||
efi_time_cap_t __user *cap_local;
|
||||
|
||||
cap_local = (efi_time_cap_t *)gettime.capabilities;
|
||||
if (put_user(cap.resolution, &(cap_local->resolution)) ||
|
||||
put_user(cap.accuracy, &(cap_local->accuracy)) ||
|
||||
put_user(cap.sets_to_zero, &(cap_local->sets_to_zero)))
|
||||
return -EFAULT;
|
||||
}
|
||||
if (gettime.time) {
|
||||
if (copy_to_user(gettime.time, &efi_time, sizeof(efi_time_t)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long efi_runtime_set_time(unsigned long arg)
|
||||
{
|
||||
struct efi_settime __user *settime_user;
|
||||
struct efi_settime settime;
|
||||
efi_status_t status;
|
||||
efi_time_t efi_time;
|
||||
|
||||
settime_user = (struct efi_settime __user *)arg;
|
||||
if (copy_from_user(&settime, settime_user, sizeof(settime)))
|
||||
return -EFAULT;
|
||||
if (copy_from_user(&efi_time, settime.time,
|
||||
sizeof(efi_time_t)))
|
||||
return -EFAULT;
|
||||
status = efi.set_time(&efi_time);
|
||||
|
||||
if (put_user(status, settime.status))
|
||||
return -EFAULT;
|
||||
|
||||
return status == EFI_SUCCESS ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
static long efi_runtime_get_waketime(unsigned long arg)
|
||||
{
|
||||
struct efi_getwakeuptime __user *getwakeuptime_user;
|
||||
struct efi_getwakeuptime getwakeuptime;
|
||||
efi_bool_t enabled, pending;
|
||||
efi_status_t status;
|
||||
efi_time_t efi_time;
|
||||
|
||||
getwakeuptime_user = (struct efi_getwakeuptime __user *)arg;
|
||||
if (copy_from_user(&getwakeuptime, getwakeuptime_user,
|
||||
sizeof(getwakeuptime)))
|
||||
return -EFAULT;
|
||||
|
||||
status = efi.get_wakeup_time(
|
||||
getwakeuptime.enabled ? (efi_bool_t *)&enabled : NULL,
|
||||
getwakeuptime.pending ? (efi_bool_t *)&pending : NULL,
|
||||
getwakeuptime.time ? &efi_time : NULL);
|
||||
|
||||
if (put_user(status, getwakeuptime.status))
|
||||
return -EFAULT;
|
||||
|
||||
if (status != EFI_SUCCESS)
|
||||
return -EINVAL;
|
||||
|
||||
if (getwakeuptime.enabled && put_user(enabled,
|
||||
getwakeuptime.enabled))
|
||||
return -EFAULT;
|
||||
|
||||
if (getwakeuptime.time) {
|
||||
if (copy_to_user(getwakeuptime.time, &efi_time,
|
||||
sizeof(efi_time_t)))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long efi_runtime_set_waketime(unsigned long arg)
|
||||
{
|
||||
struct efi_setwakeuptime __user *setwakeuptime_user;
|
||||
struct efi_setwakeuptime setwakeuptime;
|
||||
efi_bool_t enabled;
|
||||
efi_status_t status;
|
||||
efi_time_t efi_time;
|
||||
|
||||
setwakeuptime_user = (struct efi_setwakeuptime __user *)arg;
|
||||
|
||||
if (copy_from_user(&setwakeuptime, setwakeuptime_user,
|
||||
sizeof(setwakeuptime)))
|
||||
return -EFAULT;
|
||||
|
||||
enabled = setwakeuptime.enabled;
|
||||
if (setwakeuptime.time) {
|
||||
if (copy_from_user(&efi_time, setwakeuptime.time,
|
||||
sizeof(efi_time_t)))
|
||||
return -EFAULT;
|
||||
|
||||
status = efi.set_wakeup_time(enabled, &efi_time);
|
||||
} else
|
||||
status = efi.set_wakeup_time(enabled, NULL);
|
||||
|
||||
if (put_user(status, setwakeuptime.status))
|
||||
return -EFAULT;
|
||||
|
||||
return status == EFI_SUCCESS ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
static long efi_runtime_get_nextvariablename(unsigned long arg)
|
||||
{
|
||||
struct efi_getnextvariablename __user *getnextvariablename_user;
|
||||
struct efi_getnextvariablename getnextvariablename;
|
||||
unsigned long name_size, prev_name_size = 0, *ns = NULL;
|
||||
efi_status_t status;
|
||||
efi_guid_t *vd = NULL;
|
||||
efi_guid_t vendor_guid;
|
||||
efi_char16_t *name = NULL;
|
||||
int rv;
|
||||
|
||||
getnextvariablename_user = (struct efi_getnextvariablename __user *)arg;
|
||||
|
||||
if (copy_from_user(&getnextvariablename, getnextvariablename_user,
|
||||
sizeof(getnextvariablename)))
|
||||
return -EFAULT;
|
||||
|
||||
if (getnextvariablename.variable_name_size) {
|
||||
if (get_user(name_size, getnextvariablename.variable_name_size))
|
||||
return -EFAULT;
|
||||
ns = &name_size;
|
||||
prev_name_size = name_size;
|
||||
}
|
||||
|
||||
if (getnextvariablename.vendor_guid) {
|
||||
if (copy_from_user(&vendor_guid,
|
||||
getnextvariablename.vendor_guid,
|
||||
sizeof(vendor_guid)))
|
||||
return -EFAULT;
|
||||
vd = &vendor_guid;
|
||||
}
|
||||
|
||||
if (getnextvariablename.variable_name) {
|
||||
size_t name_string_size = 0;
|
||||
|
||||
rv = get_ucs2_strsize_from_user(
|
||||
getnextvariablename.variable_name,
|
||||
&name_string_size);
|
||||
if (rv)
|
||||
return rv;
|
||||
/*
|
||||
* The name_size may be smaller than the real buffer size where
|
||||
* variable name located in some use cases. The most typical
|
||||
* case is passing a 0 to get the required buffer size for the
|
||||
* 1st time call. So we need to copy the content from user
|
||||
* space for at least the string size of variable name, or else
|
||||
* the name passed to UEFI may not be terminated as we expected.
|
||||
*/
|
||||
rv = copy_ucs2_from_user_len(&name,
|
||||
getnextvariablename.variable_name,
|
||||
prev_name_size > name_string_size ?
|
||||
prev_name_size : name_string_size);
|
||||
if (rv)
|
||||
return rv;
|
||||
}
|
||||
|
||||
status = efi.get_next_variable(ns, name, vd);
|
||||
|
||||
if (put_user(status, getnextvariablename.status)) {
|
||||
rv = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
if (status == EFI_BUFFER_TOO_SMALL) {
|
||||
if (ns && put_user(*ns,
|
||||
getnextvariablename.variable_name_size)) {
|
||||
rv = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
rv = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (name) {
|
||||
if (copy_ucs2_to_user_len(getnextvariablename.variable_name,
|
||||
name, prev_name_size)) {
|
||||
rv = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (ns) {
|
||||
if (put_user(*ns, getnextvariablename.variable_name_size)) {
|
||||
rv = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (vd) {
|
||||
if (copy_to_user(getnextvariablename.vendor_guid, vd,
|
||||
sizeof(efi_guid_t)))
|
||||
rv = -EFAULT;
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(name);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static long efi_runtime_get_nexthighmonocount(unsigned long arg)
|
||||
{
|
||||
struct efi_getnexthighmonotoniccount __user *getnexthighmonocount_user;
|
||||
struct efi_getnexthighmonotoniccount getnexthighmonocount;
|
||||
efi_status_t status;
|
||||
u32 count;
|
||||
|
||||
getnexthighmonocount_user = (struct
|
||||
efi_getnexthighmonotoniccount __user *)arg;
|
||||
|
||||
if (copy_from_user(&getnexthighmonocount,
|
||||
getnexthighmonocount_user,
|
||||
sizeof(getnexthighmonocount)))
|
||||
return -EFAULT;
|
||||
|
||||
status = efi.get_next_high_mono_count(
|
||||
getnexthighmonocount.high_count ? &count : NULL);
|
||||
|
||||
if (put_user(status, getnexthighmonocount.status))
|
||||
return -EFAULT;
|
||||
|
||||
if (status != EFI_SUCCESS)
|
||||
return -EINVAL;
|
||||
|
||||
if (getnexthighmonocount.high_count &&
|
||||
put_user(count, getnexthighmonocount.high_count))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long efi_runtime_query_variableinfo(unsigned long arg)
|
||||
{
|
||||
struct efi_queryvariableinfo __user *queryvariableinfo_user;
|
||||
struct efi_queryvariableinfo queryvariableinfo;
|
||||
efi_status_t status;
|
||||
u64 max_storage, remaining, max_size;
|
||||
|
||||
queryvariableinfo_user = (struct efi_queryvariableinfo __user *)arg;
|
||||
|
||||
if (copy_from_user(&queryvariableinfo, queryvariableinfo_user,
|
||||
sizeof(queryvariableinfo)))
|
||||
return -EFAULT;
|
||||
|
||||
status = efi.query_variable_info(queryvariableinfo.attributes,
|
||||
&max_storage, &remaining, &max_size);
|
||||
|
||||
if (put_user(status, queryvariableinfo.status))
|
||||
return -EFAULT;
|
||||
|
||||
if (status != EFI_SUCCESS)
|
||||
return -EINVAL;
|
||||
|
||||
if (put_user(max_storage,
|
||||
queryvariableinfo.maximum_variable_storage_size))
|
||||
return -EFAULT;
|
||||
|
||||
if (put_user(remaining,
|
||||
queryvariableinfo.remaining_variable_storage_size))
|
||||
return -EFAULT;
|
||||
|
||||
if (put_user(max_size, queryvariableinfo.maximum_variable_size))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long efi_runtime_query_capsulecaps(unsigned long arg)
|
||||
{
|
||||
struct efi_querycapsulecapabilities __user *qcaps_user;
|
||||
struct efi_querycapsulecapabilities qcaps;
|
||||
efi_capsule_header_t *capsules;
|
||||
efi_status_t status;
|
||||
u64 max_size;
|
||||
int i, reset_type;
|
||||
int rv = 0;
|
||||
|
||||
qcaps_user = (struct efi_querycapsulecapabilities __user *)arg;
|
||||
|
||||
if (copy_from_user(&qcaps, qcaps_user, sizeof(qcaps)))
|
||||
return -EFAULT;
|
||||
|
||||
capsules = kcalloc(qcaps.capsule_count + 1,
|
||||
sizeof(efi_capsule_header_t), GFP_KERNEL);
|
||||
if (!capsules)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < qcaps.capsule_count; i++) {
|
||||
efi_capsule_header_t *c;
|
||||
/*
|
||||
* We cannot dereference qcaps.capsule_header_array directly to
|
||||
* obtain the address of the capsule as it resides in the
|
||||
* user space
|
||||
*/
|
||||
if (get_user(c, qcaps.capsule_header_array + i)) {
|
||||
rv = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
if (copy_from_user(&capsules[i], c,
|
||||
sizeof(efi_capsule_header_t))) {
|
||||
rv = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
qcaps.capsule_header_array = &capsules;
|
||||
|
||||
status = efi.query_capsule_caps((efi_capsule_header_t **)
|
||||
qcaps.capsule_header_array,
|
||||
qcaps.capsule_count,
|
||||
&max_size, &reset_type);
|
||||
|
||||
if (put_user(status, qcaps.status)) {
|
||||
rv = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
rv = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (put_user(max_size, qcaps.maximum_capsule_size)) {
|
||||
rv = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (put_user(reset_type, qcaps.reset_type))
|
||||
rv = -EFAULT;
|
||||
|
||||
out:
|
||||
kfree(capsules);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static long efi_test_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
switch (cmd) {
|
||||
case EFI_RUNTIME_GET_VARIABLE:
|
||||
return efi_runtime_get_variable(arg);
|
||||
|
||||
case EFI_RUNTIME_SET_VARIABLE:
|
||||
return efi_runtime_set_variable(arg);
|
||||
|
||||
case EFI_RUNTIME_GET_TIME:
|
||||
return efi_runtime_get_time(arg);
|
||||
|
||||
case EFI_RUNTIME_SET_TIME:
|
||||
return efi_runtime_set_time(arg);
|
||||
|
||||
case EFI_RUNTIME_GET_WAKETIME:
|
||||
return efi_runtime_get_waketime(arg);
|
||||
|
||||
case EFI_RUNTIME_SET_WAKETIME:
|
||||
return efi_runtime_set_waketime(arg);
|
||||
|
||||
case EFI_RUNTIME_GET_NEXTVARIABLENAME:
|
||||
return efi_runtime_get_nextvariablename(arg);
|
||||
|
||||
case EFI_RUNTIME_GET_NEXTHIGHMONOTONICCOUNT:
|
||||
return efi_runtime_get_nexthighmonocount(arg);
|
||||
|
||||
case EFI_RUNTIME_QUERY_VARIABLEINFO:
|
||||
return efi_runtime_query_variableinfo(arg);
|
||||
|
||||
case EFI_RUNTIME_QUERY_CAPSULECAPABILITIES:
|
||||
return efi_runtime_query_capsulecaps(arg);
|
||||
}
|
||||
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
static int efi_test_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
/*
|
||||
* nothing special to do here
|
||||
* We do accept multiple open files at the same time as we
|
||||
* synchronize on the per call operation.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int efi_test_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The various file operations we support.
|
||||
*/
|
||||
static const struct file_operations efi_test_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = efi_test_ioctl,
|
||||
.open = efi_test_open,
|
||||
.release = efi_test_close,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static struct miscdevice efi_test_dev = {
|
||||
MISC_DYNAMIC_MINOR,
|
||||
"efi_test",
|
||||
&efi_test_fops
|
||||
};
|
||||
|
||||
static int __init efi_test_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = misc_register(&efi_test_dev);
|
||||
if (ret) {
|
||||
pr_err("efi_test: can't misc_register on minor=%d\n",
|
||||
MISC_DYNAMIC_MINOR);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit efi_test_exit(void)
|
||||
{
|
||||
misc_deregister(&efi_test_dev);
|
||||
}
|
||||
|
||||
module_init(efi_test_init);
|
||||
module_exit(efi_test_exit);
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* EFI Test driver Header
|
||||
*
|
||||
* Copyright(C) 2012-2016 Canonical Ltd.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _DRIVERS_FIRMWARE_EFI_TEST_H_
|
||||
#define _DRIVERS_FIRMWARE_EFI_TEST_H_
|
||||
|
||||
#include <linux/efi.h>
|
||||
|
||||
struct efi_getvariable {
|
||||
efi_char16_t *variable_name;
|
||||
efi_guid_t *vendor_guid;
|
||||
u32 *attributes;
|
||||
unsigned long *data_size;
|
||||
void *data;
|
||||
efi_status_t *status;
|
||||
} __packed;
|
||||
|
||||
struct efi_setvariable {
|
||||
efi_char16_t *variable_name;
|
||||
efi_guid_t *vendor_guid;
|
||||
u32 attributes;
|
||||
unsigned long data_size;
|
||||
void *data;
|
||||
efi_status_t *status;
|
||||
} __packed;
|
||||
|
||||
struct efi_getnextvariablename {
|
||||
unsigned long *variable_name_size;
|
||||
efi_char16_t *variable_name;
|
||||
efi_guid_t *vendor_guid;
|
||||
efi_status_t *status;
|
||||
} __packed;
|
||||
|
||||
struct efi_queryvariableinfo {
|
||||
u32 attributes;
|
||||
u64 *maximum_variable_storage_size;
|
||||
u64 *remaining_variable_storage_size;
|
||||
u64 *maximum_variable_size;
|
||||
efi_status_t *status;
|
||||
} __packed;
|
||||
|
||||
struct efi_gettime {
|
||||
efi_time_t *time;
|
||||
efi_time_cap_t *capabilities;
|
||||
efi_status_t *status;
|
||||
} __packed;
|
||||
|
||||
struct efi_settime {
|
||||
efi_time_t *time;
|
||||
efi_status_t *status;
|
||||
} __packed;
|
||||
|
||||
struct efi_getwakeuptime {
|
||||
efi_bool_t *enabled;
|
||||
efi_bool_t *pending;
|
||||
efi_time_t *time;
|
||||
efi_status_t *status;
|
||||
} __packed;
|
||||
|
||||
struct efi_setwakeuptime {
|
||||
efi_bool_t enabled;
|
||||
efi_time_t *time;
|
||||
efi_status_t *status;
|
||||
} __packed;
|
||||
|
||||
struct efi_getnexthighmonotoniccount {
|
||||
u32 *high_count;
|
||||
efi_status_t *status;
|
||||
} __packed;
|
||||
|
||||
struct efi_querycapsulecapabilities {
|
||||
efi_capsule_header_t **capsule_header_array;
|
||||
unsigned long capsule_count;
|
||||
u64 *maximum_capsule_size;
|
||||
int *reset_type;
|
||||
efi_status_t *status;
|
||||
} __packed;
|
||||
|
||||
#define EFI_RUNTIME_GET_VARIABLE \
|
||||
_IOWR('p', 0x01, struct efi_getvariable)
|
||||
#define EFI_RUNTIME_SET_VARIABLE \
|
||||
_IOW('p', 0x02, struct efi_setvariable)
|
||||
|
||||
#define EFI_RUNTIME_GET_TIME \
|
||||
_IOR('p', 0x03, struct efi_gettime)
|
||||
#define EFI_RUNTIME_SET_TIME \
|
||||
_IOW('p', 0x04, struct efi_settime)
|
||||
|
||||
#define EFI_RUNTIME_GET_WAKETIME \
|
||||
_IOR('p', 0x05, struct efi_getwakeuptime)
|
||||
#define EFI_RUNTIME_SET_WAKETIME \
|
||||
_IOW('p', 0x06, struct efi_setwakeuptime)
|
||||
|
||||
#define EFI_RUNTIME_GET_NEXTVARIABLENAME \
|
||||
_IOWR('p', 0x07, struct efi_getnextvariablename)
|
||||
|
||||
#define EFI_RUNTIME_QUERY_VARIABLEINFO \
|
||||
_IOR('p', 0x08, struct efi_queryvariableinfo)
|
||||
|
||||
#define EFI_RUNTIME_GET_NEXTHIGHMONOTONICCOUNT \
|
||||
_IOR('p', 0x09, struct efi_getnexthighmonotoniccount)
|
||||
|
||||
#define EFI_RUNTIME_QUERY_CAPSULECAPABILITIES \
|
||||
_IOR('p', 0x0A, struct efi_querycapsulecapabilities)
|
||||
|
||||
#endif /* _DRIVERS_FIRMWARE_EFI_TEST_H_ */
|
|
@ -37,6 +37,14 @@
|
|||
/* Private pointer to registered efivars */
|
||||
static struct efivars *__efivars;
|
||||
|
||||
/*
|
||||
* efivars_lock protects three things:
|
||||
* 1) efivarfs_list and efivars_sysfs_list
|
||||
* 2) ->ops calls
|
||||
* 3) (un)registration of __efivars
|
||||
*/
|
||||
static DEFINE_SEMAPHORE(efivars_lock);
|
||||
|
||||
static bool efivar_wq_enabled = true;
|
||||
DECLARE_WORK(efivar_work, NULL);
|
||||
EXPORT_SYMBOL_GPL(efivar_work);
|
||||
|
@ -434,7 +442,10 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spin_lock_irq(&__efivars->lock);
|
||||
if (down_interruptible(&efivars_lock)) {
|
||||
err = -EINTR;
|
||||
goto free;
|
||||
}
|
||||
|
||||
/*
|
||||
* Per EFI spec, the maximum storage allocated for both
|
||||
|
@ -450,7 +461,7 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
|
|||
switch (status) {
|
||||
case EFI_SUCCESS:
|
||||
if (duplicates)
|
||||
spin_unlock_irq(&__efivars->lock);
|
||||
up(&efivars_lock);
|
||||
|
||||
variable_name_size = var_name_strnsize(variable_name,
|
||||
variable_name_size);
|
||||
|
@ -476,8 +487,12 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
|
|||
status = EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (duplicates)
|
||||
spin_lock_irq(&__efivars->lock);
|
||||
if (duplicates) {
|
||||
if (down_interruptible(&efivars_lock)) {
|
||||
err = -EINTR;
|
||||
goto free;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case EFI_NOT_FOUND:
|
||||
|
@ -491,8 +506,8 @@ int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
|
|||
|
||||
} while (status != EFI_NOT_FOUND);
|
||||
|
||||
spin_unlock_irq(&__efivars->lock);
|
||||
|
||||
up(&efivars_lock);
|
||||
free:
|
||||
kfree(variable_name);
|
||||
|
||||
return err;
|
||||
|
@ -503,24 +518,34 @@ EXPORT_SYMBOL_GPL(efivar_init);
|
|||
* efivar_entry_add - add entry to variable list
|
||||
* @entry: entry to add to list
|
||||
* @head: list head
|
||||
*
|
||||
* Returns 0 on success, or a kernel error code on failure.
|
||||
*/
|
||||
void efivar_entry_add(struct efivar_entry *entry, struct list_head *head)
|
||||
int efivar_entry_add(struct efivar_entry *entry, struct list_head *head)
|
||||
{
|
||||
spin_lock_irq(&__efivars->lock);
|
||||
if (down_interruptible(&efivars_lock))
|
||||
return -EINTR;
|
||||
list_add(&entry->list, head);
|
||||
spin_unlock_irq(&__efivars->lock);
|
||||
up(&efivars_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(efivar_entry_add);
|
||||
|
||||
/**
|
||||
* efivar_entry_remove - remove entry from variable list
|
||||
* @entry: entry to remove from list
|
||||
*
|
||||
* Returns 0 on success, or a kernel error code on failure.
|
||||
*/
|
||||
void efivar_entry_remove(struct efivar_entry *entry)
|
||||
int efivar_entry_remove(struct efivar_entry *entry)
|
||||
{
|
||||
spin_lock_irq(&__efivars->lock);
|
||||
if (down_interruptible(&efivars_lock))
|
||||
return -EINTR;
|
||||
list_del(&entry->list);
|
||||
spin_unlock_irq(&__efivars->lock);
|
||||
up(&efivars_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(efivar_entry_remove);
|
||||
|
||||
|
@ -537,10 +562,8 @@ EXPORT_SYMBOL_GPL(efivar_entry_remove);
|
|||
*/
|
||||
static void efivar_entry_list_del_unlock(struct efivar_entry *entry)
|
||||
{
|
||||
lockdep_assert_held(&__efivars->lock);
|
||||
|
||||
list_del(&entry->list);
|
||||
spin_unlock_irq(&__efivars->lock);
|
||||
up(&efivars_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -563,8 +586,6 @@ int __efivar_entry_delete(struct efivar_entry *entry)
|
|||
const struct efivar_operations *ops = __efivars->ops;
|
||||
efi_status_t status;
|
||||
|
||||
lockdep_assert_held(&__efivars->lock);
|
||||
|
||||
status = ops->set_variable(entry->var.VariableName,
|
||||
&entry->var.VendorGuid,
|
||||
0, 0, NULL);
|
||||
|
@ -581,20 +602,22 @@ EXPORT_SYMBOL_GPL(__efivar_entry_delete);
|
|||
* variable list. It is the caller's responsibility to free @entry
|
||||
* once we return.
|
||||
*
|
||||
* Returns 0 on success, or a converted EFI status code if
|
||||
* set_variable() fails.
|
||||
* Returns 0 on success, -EINTR if we can't grab the semaphore,
|
||||
* converted EFI status code if set_variable() fails.
|
||||
*/
|
||||
int efivar_entry_delete(struct efivar_entry *entry)
|
||||
{
|
||||
const struct efivar_operations *ops = __efivars->ops;
|
||||
efi_status_t status;
|
||||
|
||||
spin_lock_irq(&__efivars->lock);
|
||||
if (down_interruptible(&efivars_lock))
|
||||
return -EINTR;
|
||||
|
||||
status = ops->set_variable(entry->var.VariableName,
|
||||
&entry->var.VendorGuid,
|
||||
0, 0, NULL);
|
||||
if (!(status == EFI_SUCCESS || status == EFI_NOT_FOUND)) {
|
||||
spin_unlock_irq(&__efivars->lock);
|
||||
up(&efivars_lock);
|
||||
return efi_status_to_err(status);
|
||||
}
|
||||
|
||||
|
@ -620,9 +643,9 @@ EXPORT_SYMBOL_GPL(efivar_entry_delete);
|
|||
* If @head is not NULL a lookup is performed to determine whether
|
||||
* the entry is already on the list.
|
||||
*
|
||||
* Returns 0 on success, -EEXIST if a lookup is performed and the entry
|
||||
* already exists on the list, or a converted EFI status code if
|
||||
* set_variable() fails.
|
||||
* Returns 0 on success, -EINTR if we can't grab the semaphore,
|
||||
* -EEXIST if a lookup is performed and the entry already exists on
|
||||
* the list, or a converted EFI status code if set_variable() fails.
|
||||
*/
|
||||
int efivar_entry_set(struct efivar_entry *entry, u32 attributes,
|
||||
unsigned long size, void *data, struct list_head *head)
|
||||
|
@ -632,10 +655,10 @@ int efivar_entry_set(struct efivar_entry *entry, u32 attributes,
|
|||
efi_char16_t *name = entry->var.VariableName;
|
||||
efi_guid_t vendor = entry->var.VendorGuid;
|
||||
|
||||
spin_lock_irq(&__efivars->lock);
|
||||
|
||||
if (down_interruptible(&efivars_lock))
|
||||
return -EINTR;
|
||||
if (head && efivar_entry_find(name, vendor, head, false)) {
|
||||
spin_unlock_irq(&__efivars->lock);
|
||||
up(&efivars_lock);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
|
@ -644,7 +667,7 @@ int efivar_entry_set(struct efivar_entry *entry, u32 attributes,
|
|||
status = ops->set_variable(name, &vendor,
|
||||
attributes, size, data);
|
||||
|
||||
spin_unlock_irq(&__efivars->lock);
|
||||
up(&efivars_lock);
|
||||
|
||||
return efi_status_to_err(status);
|
||||
|
||||
|
@ -658,30 +681,29 @@ EXPORT_SYMBOL_GPL(efivar_entry_set);
|
|||
* from crash/panic handlers.
|
||||
*
|
||||
* Crucially, this function will not block if it cannot acquire
|
||||
* __efivars->lock. Instead, it returns -EBUSY.
|
||||
* efivars_lock. Instead, it returns -EBUSY.
|
||||
*/
|
||||
static int
|
||||
efivar_entry_set_nonblocking(efi_char16_t *name, efi_guid_t vendor,
|
||||
u32 attributes, unsigned long size, void *data)
|
||||
{
|
||||
const struct efivar_operations *ops = __efivars->ops;
|
||||
unsigned long flags;
|
||||
efi_status_t status;
|
||||
|
||||
if (!spin_trylock_irqsave(&__efivars->lock, flags))
|
||||
if (down_trylock(&efivars_lock))
|
||||
return -EBUSY;
|
||||
|
||||
status = check_var_size_nonblocking(attributes,
|
||||
size + ucs2_strsize(name, 1024));
|
||||
if (status != EFI_SUCCESS) {
|
||||
spin_unlock_irqrestore(&__efivars->lock, flags);
|
||||
up(&efivars_lock);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
status = ops->set_variable_nonblocking(name, &vendor, attributes,
|
||||
size, data);
|
||||
|
||||
spin_unlock_irqrestore(&__efivars->lock, flags);
|
||||
up(&efivars_lock);
|
||||
return efi_status_to_err(status);
|
||||
}
|
||||
|
||||
|
@ -706,7 +728,6 @@ int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes,
|
|||
bool block, unsigned long size, void *data)
|
||||
{
|
||||
const struct efivar_operations *ops = __efivars->ops;
|
||||
unsigned long flags;
|
||||
efi_status_t status;
|
||||
|
||||
if (!ops->query_variable_store)
|
||||
|
@ -727,21 +748,22 @@ int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes,
|
|||
size, data);
|
||||
|
||||
if (!block) {
|
||||
if (!spin_trylock_irqsave(&__efivars->lock, flags))
|
||||
if (down_trylock(&efivars_lock))
|
||||
return -EBUSY;
|
||||
} else {
|
||||
spin_lock_irqsave(&__efivars->lock, flags);
|
||||
if (down_interruptible(&efivars_lock))
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
status = check_var_size(attributes, size + ucs2_strsize(name, 1024));
|
||||
if (status != EFI_SUCCESS) {
|
||||
spin_unlock_irqrestore(&__efivars->lock, flags);
|
||||
up(&efivars_lock);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
status = ops->set_variable(name, &vendor, attributes, size, data);
|
||||
|
||||
spin_unlock_irqrestore(&__efivars->lock, flags);
|
||||
up(&efivars_lock);
|
||||
|
||||
return efi_status_to_err(status);
|
||||
}
|
||||
|
@ -771,8 +793,6 @@ struct efivar_entry *efivar_entry_find(efi_char16_t *name, efi_guid_t guid,
|
|||
int strsize1, strsize2;
|
||||
bool found = false;
|
||||
|
||||
lockdep_assert_held(&__efivars->lock);
|
||||
|
||||
list_for_each_entry_safe(entry, n, head, list) {
|
||||
strsize1 = ucs2_strsize(name, 1024);
|
||||
strsize2 = ucs2_strsize(entry->var.VariableName, 1024);
|
||||
|
@ -814,10 +834,11 @@ int efivar_entry_size(struct efivar_entry *entry, unsigned long *size)
|
|||
|
||||
*size = 0;
|
||||
|
||||
spin_lock_irq(&__efivars->lock);
|
||||
if (down_interruptible(&efivars_lock))
|
||||
return -EINTR;
|
||||
status = ops->get_variable(entry->var.VariableName,
|
||||
&entry->var.VendorGuid, NULL, size, NULL);
|
||||
spin_unlock_irq(&__efivars->lock);
|
||||
up(&efivars_lock);
|
||||
|
||||
if (status != EFI_BUFFER_TOO_SMALL)
|
||||
return efi_status_to_err(status);
|
||||
|
@ -843,8 +864,6 @@ int __efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
|
|||
const struct efivar_operations *ops = __efivars->ops;
|
||||
efi_status_t status;
|
||||
|
||||
lockdep_assert_held(&__efivars->lock);
|
||||
|
||||
status = ops->get_variable(entry->var.VariableName,
|
||||
&entry->var.VendorGuid,
|
||||
attributes, size, data);
|
||||
|
@ -866,11 +885,12 @@ int efivar_entry_get(struct efivar_entry *entry, u32 *attributes,
|
|||
const struct efivar_operations *ops = __efivars->ops;
|
||||
efi_status_t status;
|
||||
|
||||
spin_lock_irq(&__efivars->lock);
|
||||
if (down_interruptible(&efivars_lock))
|
||||
return -EINTR;
|
||||
status = ops->get_variable(entry->var.VariableName,
|
||||
&entry->var.VendorGuid,
|
||||
attributes, size, data);
|
||||
spin_unlock_irq(&__efivars->lock);
|
||||
up(&efivars_lock);
|
||||
|
||||
return efi_status_to_err(status);
|
||||
}
|
||||
|
@ -917,7 +937,8 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
|
|||
* set_variable call, and removal of the variable from the efivars
|
||||
* list (in the case of an authenticated delete).
|
||||
*/
|
||||
spin_lock_irq(&__efivars->lock);
|
||||
if (down_interruptible(&efivars_lock))
|
||||
return -EINTR;
|
||||
|
||||
/*
|
||||
* Ensure that the available space hasn't shrunk below the safe level
|
||||
|
@ -957,7 +978,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
|
|||
if (status == EFI_NOT_FOUND)
|
||||
efivar_entry_list_del_unlock(entry);
|
||||
else
|
||||
spin_unlock_irq(&__efivars->lock);
|
||||
up(&efivars_lock);
|
||||
|
||||
if (status && status != EFI_BUFFER_TOO_SMALL)
|
||||
return efi_status_to_err(status);
|
||||
|
@ -965,7 +986,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
|
|||
return 0;
|
||||
|
||||
out:
|
||||
spin_unlock_irq(&__efivars->lock);
|
||||
up(&efivars_lock);
|
||||
return err;
|
||||
|
||||
}
|
||||
|
@ -978,9 +999,9 @@ EXPORT_SYMBOL_GPL(efivar_entry_set_get_size);
|
|||
* efivar_entry_iter_end() is called. This function is usually used in
|
||||
* conjunction with __efivar_entry_iter() or efivar_entry_iter().
|
||||
*/
|
||||
void efivar_entry_iter_begin(void)
|
||||
int efivar_entry_iter_begin(void)
|
||||
{
|
||||
spin_lock_irq(&__efivars->lock);
|
||||
return down_interruptible(&efivars_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(efivar_entry_iter_begin);
|
||||
|
||||
|
@ -991,7 +1012,7 @@ EXPORT_SYMBOL_GPL(efivar_entry_iter_begin);
|
|||
*/
|
||||
void efivar_entry_iter_end(void)
|
||||
{
|
||||
spin_unlock_irq(&__efivars->lock);
|
||||
up(&efivars_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(efivar_entry_iter_end);
|
||||
|
||||
|
@ -1067,7 +1088,9 @@ int efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
|
|||
{
|
||||
int err = 0;
|
||||
|
||||
efivar_entry_iter_begin();
|
||||
err = efivar_entry_iter_begin();
|
||||
if (err)
|
||||
return err;
|
||||
err = __efivar_entry_iter(func, head, data, NULL);
|
||||
efivar_entry_iter_end();
|
||||
|
||||
|
@ -1112,12 +1135,18 @@ int efivars_register(struct efivars *efivars,
|
|||
const struct efivar_operations *ops,
|
||||
struct kobject *kobject)
|
||||
{
|
||||
spin_lock_init(&efivars->lock);
|
||||
if (down_interruptible(&efivars_lock))
|
||||
return -EINTR;
|
||||
|
||||
efivars->ops = ops;
|
||||
efivars->kobject = kobject;
|
||||
|
||||
__efivars = efivars;
|
||||
|
||||
pr_info("Registered efivars operations\n");
|
||||
|
||||
up(&efivars_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(efivars_register);
|
||||
|
@ -1133,6 +1162,9 @@ int efivars_unregister(struct efivars *efivars)
|
|||
{
|
||||
int rv;
|
||||
|
||||
if (down_interruptible(&efivars_lock))
|
||||
return -EINTR;
|
||||
|
||||
if (!__efivars) {
|
||||
printk(KERN_ERR "efivars not registered\n");
|
||||
rv = -EINVAL;
|
||||
|
@ -1144,10 +1176,12 @@ int efivars_unregister(struct efivars *efivars)
|
|||
goto out;
|
||||
}
|
||||
|
||||
pr_info("Unregistered efivars operations\n");
|
||||
__efivars = NULL;
|
||||
|
||||
rv = 0;
|
||||
out:
|
||||
up(&efivars_lock);
|
||||
return rv;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(efivars_unregister);
|
||||
|
|
|
@ -910,8 +910,7 @@ out_err:
|
|||
gsmi_buf_free(gsmi_dev.param_buf);
|
||||
gsmi_buf_free(gsmi_dev.data_buf);
|
||||
gsmi_buf_free(gsmi_dev.name_buf);
|
||||
if (gsmi_dev.dma_pool)
|
||||
dma_pool_destroy(gsmi_dev.dma_pool);
|
||||
dma_pool_destroy(gsmi_dev.dma_pool);
|
||||
platform_device_unregister(gsmi_dev.pdev);
|
||||
pr_info("gsmi: failed to load: %d\n", ret);
|
||||
return ret;
|
||||
|
|
|
@ -105,7 +105,10 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry,
|
|||
|
||||
inode->i_private = var;
|
||||
|
||||
efivar_entry_add(var, &efivarfs_list);
|
||||
err = efivar_entry_add(var, &efivarfs_list);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
dget(dentry);
|
||||
out:
|
||||
|
|
|
@ -157,12 +157,14 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,
|
|||
goto fail_inode;
|
||||
}
|
||||
|
||||
efivar_entry_size(entry, &size);
|
||||
err = efivar_entry_add(entry, &efivarfs_list);
|
||||
if (err)
|
||||
goto fail_inode;
|
||||
|
||||
/* copied by the above to local storage in the dentry. */
|
||||
kfree(name);
|
||||
|
||||
efivar_entry_size(entry, &size);
|
||||
efivar_entry_add(entry, &efivarfs_list);
|
||||
|
||||
inode_lock(inode);
|
||||
inode->i_private = entry;
|
||||
i_size_write(inode, size + sizeof(entry->var.Attributes));
|
||||
|
@ -182,7 +184,10 @@ fail:
|
|||
|
||||
static int efivarfs_destroy(struct efivar_entry *entry, void *data)
|
||||
{
|
||||
efivar_entry_remove(entry);
|
||||
int err = efivar_entry_remove(entry);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
kfree(entry);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/ioport.h>
|
||||
#include <linux/pfn.h>
|
||||
#include <linux/pstore.h>
|
||||
#include <linux/range.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/uuid.h>
|
||||
#include <linux/screen_info.h>
|
||||
|
@ -37,6 +38,7 @@
|
|||
#define EFI_WRITE_PROTECTED ( 8 | (1UL << (BITS_PER_LONG-1)))
|
||||
#define EFI_OUT_OF_RESOURCES ( 9 | (1UL << (BITS_PER_LONG-1)))
|
||||
#define EFI_NOT_FOUND (14 | (1UL << (BITS_PER_LONG-1)))
|
||||
#define EFI_ABORTED (21 | (1UL << (BITS_PER_LONG-1)))
|
||||
#define EFI_SECURITY_VIOLATION (26 | (1UL << (BITS_PER_LONG-1)))
|
||||
|
||||
typedef unsigned long efi_status_t;
|
||||
|
@ -678,6 +680,18 @@ typedef struct {
|
|||
unsigned long tables;
|
||||
} efi_system_table_t;
|
||||
|
||||
/*
|
||||
* Architecture independent structure for describing a memory map for the
|
||||
* benefit of efi_memmap_init_early(), saving us the need to pass four
|
||||
* parameters.
|
||||
*/
|
||||
struct efi_memory_map_data {
|
||||
phys_addr_t phys_map;
|
||||
unsigned long size;
|
||||
unsigned long desc_version;
|
||||
unsigned long desc_size;
|
||||
};
|
||||
|
||||
struct efi_memory_map {
|
||||
phys_addr_t phys_map;
|
||||
void *map;
|
||||
|
@ -685,6 +699,12 @@ struct efi_memory_map {
|
|||
int nr_map;
|
||||
unsigned long desc_version;
|
||||
unsigned long desc_size;
|
||||
bool late;
|
||||
};
|
||||
|
||||
struct efi_mem_range {
|
||||
struct range range;
|
||||
u64 attribute;
|
||||
};
|
||||
|
||||
struct efi_fdt_params {
|
||||
|
@ -909,6 +929,16 @@ static inline efi_status_t efi_query_variable_store(u32 attributes,
|
|||
}
|
||||
#endif
|
||||
extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr);
|
||||
|
||||
extern int __init efi_memmap_init_early(struct efi_memory_map_data *data);
|
||||
extern int __init efi_memmap_init_late(phys_addr_t addr, unsigned long size);
|
||||
extern void __init efi_memmap_unmap(void);
|
||||
extern int __init efi_memmap_install(phys_addr_t addr, unsigned int nr_map);
|
||||
extern int __init efi_memmap_split_count(efi_memory_desc_t *md,
|
||||
struct range *range);
|
||||
extern void __init efi_memmap_insert(struct efi_memory_map *old_memmap,
|
||||
void *buf, struct efi_mem_range *mem);
|
||||
|
||||
extern int efi_config_init(efi_config_table_type_t *arch_tables);
|
||||
#ifdef CONFIG_EFI_ESRT
|
||||
extern void __init efi_esrt_init(void);
|
||||
|
@ -924,6 +954,7 @@ extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size);
|
|||
extern int __init efi_uart_console_only (void);
|
||||
extern u64 efi_mem_desc_end(efi_memory_desc_t *md);
|
||||
extern int efi_mem_desc_lookup(u64 phys_addr, efi_memory_desc_t *out_md);
|
||||
extern void efi_mem_reserve(phys_addr_t addr, u64 size);
|
||||
extern void efi_initialize_iomem_resources(struct resource *code_resource,
|
||||
struct resource *data_resource, struct resource *bss_resource);
|
||||
extern void efi_reserve_boot_services(void);
|
||||
|
@ -1136,12 +1167,6 @@ struct efivar_operations {
|
|||
};
|
||||
|
||||
struct efivars {
|
||||
/*
|
||||
* ->lock protects two things:
|
||||
* 1) efivarfs_list and efivars_sysfs_list
|
||||
* 2) ->ops calls
|
||||
*/
|
||||
spinlock_t lock;
|
||||
struct kset *kset;
|
||||
struct kobject *kobject;
|
||||
const struct efivar_operations *ops;
|
||||
|
@ -1282,8 +1307,8 @@ struct kobject *efivars_kobject(void);
|
|||
int efivar_init(int (*func)(efi_char16_t *, efi_guid_t, unsigned long, void *),
|
||||
void *data, bool duplicates, struct list_head *head);
|
||||
|
||||
void efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
|
||||
void efivar_entry_remove(struct efivar_entry *entry);
|
||||
int efivar_entry_add(struct efivar_entry *entry, struct list_head *head);
|
||||
int efivar_entry_remove(struct efivar_entry *entry);
|
||||
|
||||
int __efivar_entry_delete(struct efivar_entry *entry);
|
||||
int efivar_entry_delete(struct efivar_entry *entry);
|
||||
|
@ -1300,7 +1325,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
|
|||
int efivar_entry_set_safe(efi_char16_t *name, efi_guid_t vendor, u32 attributes,
|
||||
bool block, unsigned long size, void *data);
|
||||
|
||||
void efivar_entry_iter_begin(void);
|
||||
int efivar_entry_iter_begin(void);
|
||||
void efivar_entry_iter_end(void);
|
||||
|
||||
int __efivar_entry_iter(int (*func)(struct efivar_entry *, void *),
|
||||
|
@ -1336,7 +1361,6 @@ extern int efi_capsule_update(efi_capsule_header_t *capsule,
|
|||
|
||||
#ifdef CONFIG_EFI_RUNTIME_MAP
|
||||
int efi_runtime_map_init(struct kobject *);
|
||||
void efi_runtime_map_setup(void *, int, u32);
|
||||
int efi_get_runtime_map_size(void);
|
||||
int efi_get_runtime_map_desc_size(void);
|
||||
int efi_runtime_map_copy(void *buf, size_t bufsz);
|
||||
|
@ -1346,9 +1370,6 @@ static inline int efi_runtime_map_init(struct kobject *kobj)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
efi_runtime_map_setup(void *map, int nr_entries, u32 desc_size) {}
|
||||
|
||||
static inline int efi_get_runtime_map_size(void)
|
||||
{
|
||||
return 0;
|
||||
|
|
|
@ -56,7 +56,7 @@ ucs2_utf8size(const ucs2_char_t *src)
|
|||
unsigned long i;
|
||||
unsigned long j = 0;
|
||||
|
||||
for (i = 0; i < ucs2_strlen(src); i++) {
|
||||
for (i = 0; src[i]; i++) {
|
||||
u16 c = src[i];
|
||||
|
||||
if (c >= 0x800)
|
||||
|
|
Loading…
Reference in New Issue