aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArd Biesheuvel <ard.biesheuvel@linaro.org>2016-01-11 04:43:16 -0500
committerCatalin Marinas <catalin.marinas@arm.com>2016-02-24 09:57:28 -0500
commit2ddbfc81eac84a299cb4747a8764bc43f23e9008 (patch)
tree958da4ff15f5fc23a4d65a427a41537a6a747cfb
parente4fbf4767440472f9d23b0f25a2b905e1c63b6a8 (diff)
efi: stub: add implementation of efi_random_alloc()
This implements efi_random_alloc(), which allocates a chunk of memory of a certain size at a certain alignment, and uses the random_seed argument it receives to randomize the address of the allocation. This is implemented by iterating over the UEFI memory map, counting the number of suitable slots (aligned offsets) within each region, and picking a random number between 0 and 'number of slots - 1' to select the slot, This should guarantee that each possible offset is chosen equally likely. Suggested-by: Kees Cook <keescook@chromium.org> Reviewed-by: Matt Fleming <matt@codeblueprint.co.uk> Reviewed-by: Kees Cook <keescook@chromium.org> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
-rw-r--r--drivers/firmware/efi/libstub/efistub.h4
-rw-r--r--drivers/firmware/efi/libstub/random.c100
2 files changed, 104 insertions, 0 deletions
diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
index 206b7252b9d1..5ed3d3f38166 100644
--- a/drivers/firmware/efi/libstub/efistub.h
+++ b/drivers/firmware/efi/libstub/efistub.h
@@ -46,4 +46,8 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size,
46efi_status_t efi_get_random_bytes(efi_system_table_t *sys_table, 46efi_status_t efi_get_random_bytes(efi_system_table_t *sys_table,
47 unsigned long size, u8 *out); 47 unsigned long size, u8 *out);
48 48
49efi_status_t efi_random_alloc(efi_system_table_t *sys_table_arg,
50 unsigned long size, unsigned long align,
51 unsigned long *addr, unsigned long random_seed);
52
49#endif 53#endif
diff --git a/drivers/firmware/efi/libstub/random.c b/drivers/firmware/efi/libstub/random.c
index 97941ee5954f..53f6d3fe6d86 100644
--- a/drivers/firmware/efi/libstub/random.c
+++ b/drivers/firmware/efi/libstub/random.c
@@ -33,3 +33,103 @@ efi_status_t efi_get_random_bytes(efi_system_table_t *sys_table_arg,
33 33
34 return rng->get_rng(rng, NULL, size, out); 34 return rng->get_rng(rng, NULL, size, out);
35} 35}
36
37/*
38 * Return the number of slots covered by this entry, i.e., the number of
39 * addresses it covers that are suitably aligned and supply enough room
40 * for the allocation.
41 */
42static unsigned long get_entry_num_slots(efi_memory_desc_t *md,
43 unsigned long size,
44 unsigned long align)
45{
46 u64 start, end;
47
48 if (md->type != EFI_CONVENTIONAL_MEMORY)
49 return 0;
50
51 start = round_up(md->phys_addr, align);
52 end = round_down(md->phys_addr + md->num_pages * EFI_PAGE_SIZE - size,
53 align);
54
55 if (start > end)
56 return 0;
57
58 return (end - start + 1) / align;
59}
60
61/*
62 * The UEFI memory descriptors have a virtual address field that is only used
63 * when installing the virtual mapping using SetVirtualAddressMap(). Since it
64 * is unused here, we can reuse it to keep track of each descriptor's slot
65 * count.
66 */
67#define MD_NUM_SLOTS(md) ((md)->virt_addr)
68
69efi_status_t efi_random_alloc(efi_system_table_t *sys_table_arg,
70 unsigned long size,
71 unsigned long align,
72 unsigned long *addr,
73 unsigned long random_seed)
74{
75 unsigned long map_size, desc_size, total_slots = 0, target_slot;
76 efi_status_t status;
77 efi_memory_desc_t *memory_map;
78 int map_offset;
79
80 status = efi_get_memory_map(sys_table_arg, &memory_map, &map_size,
81 &desc_size, NULL, NULL);
82 if (status != EFI_SUCCESS)
83 return status;
84
85 if (align < EFI_ALLOC_ALIGN)
86 align = EFI_ALLOC_ALIGN;
87
88 /* count the suitable slots in each memory map entry */
89 for (map_offset = 0; map_offset < map_size; map_offset += desc_size) {
90 efi_memory_desc_t *md = (void *)memory_map + map_offset;
91 unsigned long slots;
92
93 slots = get_entry_num_slots(md, size, align);
94 MD_NUM_SLOTS(md) = slots;
95 total_slots += slots;
96 }
97
98 /* find a random number between 0 and total_slots */
99 target_slot = (total_slots * (u16)random_seed) >> 16;
100
101 /*
102 * target_slot is now a value in the range [0, total_slots), and so
103 * it corresponds with exactly one of the suitable slots we recorded
104 * when iterating over the memory map the first time around.
105 *
106 * So iterate over the memory map again, subtracting the number of
107 * slots of each entry at each iteration, until we have found the entry
108 * that covers our chosen slot. Use the residual value of target_slot
109 * to calculate the randomly chosen address, and allocate it directly
110 * using EFI_ALLOCATE_ADDRESS.
111 */
112 for (map_offset = 0; map_offset < map_size; map_offset += desc_size) {
113 efi_memory_desc_t *md = (void *)memory_map + map_offset;
114 efi_physical_addr_t target;
115 unsigned long pages;
116
117 if (target_slot >= MD_NUM_SLOTS(md)) {
118 target_slot -= MD_NUM_SLOTS(md);
119 continue;
120 }
121
122 target = round_up(md->phys_addr, align) + target_slot * align;
123 pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
124
125 status = efi_call_early(allocate_pages, EFI_ALLOCATE_ADDRESS,
126 EFI_LOADER_DATA, pages, &target);
127 if (status == EFI_SUCCESS)
128 *addr = target;
129 break;
130 }
131
132 efi_call_early(free_pool, memory_map);
133
134 return status;
135}