Remove grub_efi_allocate_pages.
[grub.git] / grub-core / kern / arm / efi / misc.c
1 /* misc.c - various system functions for an arm-based EFI system */
2 /*
3  *  GRUB  --  GRand Unified Bootloader
4  *  Copyright (C) 2013 Free Software Foundation, Inc.
5  *
6  *  GRUB is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  GRUB is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <grub/misc.h>
21 #include <grub/mm.h>
22 #include <grub/cpu/linux.h>
23 #include <grub/cpu/system.h>
24 #include <grub/efi/efi.h>
25 #include <grub/machine/loader.h>
26
27 static inline grub_size_t
28 page_align (grub_size_t size)
29 {
30   return (size + (1 << 12) - 1) & (~((1 << 12) - 1));
31 }
32
33 /* Find the optimal number of pages for the memory map. Is it better to
34    move this code to efi/mm.c?  */
35 static grub_efi_uintn_t
36 find_mmap_size (void)
37 {
38   static grub_efi_uintn_t mmap_size = 0;
39
40   if (mmap_size != 0)
41     return mmap_size;
42   
43   mmap_size = (1 << 12);
44   while (1)
45     {
46       int ret;
47       grub_efi_memory_descriptor_t *mmap;
48       grub_efi_uintn_t desc_size;
49       
50       mmap = grub_malloc (mmap_size);
51       if (! mmap)
52         return 0;
53
54       ret = grub_efi_get_memory_map (&mmap_size, mmap, 0, &desc_size, 0);
55       grub_free (mmap);
56       
57       if (ret < 0)
58         {
59           grub_error (GRUB_ERR_IO, "cannot get memory map");
60           return 0;
61         }
62       else if (ret > 0)
63         break;
64
65       mmap_size += (1 << 12);
66     }
67
68   /* Increase the size a bit for safety, because GRUB allocates more on
69      later, and EFI itself may allocate more.  */
70   mmap_size += (1 << 12);
71
72   return page_align (mmap_size);
73 }
74
75 #define NEXT_MEMORY_DESCRIPTOR(desc, size)      \
76   ((grub_efi_memory_descriptor_t *) ((char *) (desc) + (size)))
77 #define PAGE_SHIFT 12
78
79 void *
80 grub_efi_allocate_loader_memory (grub_uint32_t min_offset, grub_uint32_t size)
81 {
82   grub_efi_uintn_t desc_size;
83   grub_efi_memory_descriptor_t *mmap, *mmap_end;
84   grub_efi_uintn_t mmap_size, tmp_mmap_size;
85   grub_efi_memory_descriptor_t *desc;
86   void *mem = NULL;
87   grub_addr_t min_start = 0;
88
89   mmap_size = find_mmap_size();
90   if (!mmap_size)
91     return NULL;
92
93   mmap = grub_malloc(mmap_size);
94   if (!mmap)
95     return NULL;
96
97   tmp_mmap_size = mmap_size;
98   if (grub_efi_get_memory_map (&tmp_mmap_size, mmap, 0, &desc_size, 0) <= 0)
99     {
100       grub_error (GRUB_ERR_IO, "cannot get memory map");
101       goto fail;
102     }
103
104   mmap_end = NEXT_MEMORY_DESCRIPTOR (mmap, tmp_mmap_size);
105   /* Find lowest accessible RAM location */
106   {
107     int found = 0;
108     for (desc = mmap ; !found && (desc < mmap_end) ;
109          desc = NEXT_MEMORY_DESCRIPTOR(desc, desc_size))
110       {
111         switch (desc->type)
112           {
113           case GRUB_EFI_CONVENTIONAL_MEMORY:
114           case GRUB_EFI_LOADER_CODE:
115           case GRUB_EFI_LOADER_DATA:
116             min_start = desc->physical_start + min_offset;
117             found = 1;
118             break;
119           default:
120             break;
121           }
122       }
123   }
124
125   /* First, find free pages for the real mode code
126      and the memory map buffer.  */
127   for (desc = mmap ; desc < mmap_end ;
128        desc = NEXT_MEMORY_DESCRIPTOR(desc, desc_size))
129     {
130       grub_uint64_t start, end;
131
132       grub_dprintf("mm", "%s: 0x%08x bytes @ 0x%08x\n",
133                    __FUNCTION__,
134                    (grub_uint32_t) (desc->num_pages << PAGE_SHIFT),
135                    (grub_uint32_t) (desc->physical_start));
136
137       if (desc->type != GRUB_EFI_CONVENTIONAL_MEMORY)
138         continue;
139
140       start = desc->physical_start;
141       end = start + (desc->num_pages << PAGE_SHIFT);
142       grub_dprintf("mm", "%s: start=0x%016llx, end=0x%016llx\n",
143                   __FUNCTION__, start, end);
144       start = start < min_start ? min_start : start;
145       if (start + size > end)
146         continue;
147       grub_dprintf("mm", "%s: let's allocate some (0x%x) pages @ 0x%08x...\n",
148                   __FUNCTION__, (size >> PAGE_SHIFT), (grub_addr_t) start);
149       mem = grub_efi_allocate_fixed (start, (size >> PAGE_SHIFT) + 1);
150       grub_dprintf("mm", "%s: retval=0x%08x\n",
151                    __FUNCTION__, (grub_addr_t) mem);
152       if (! mem)
153         {
154           grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory");
155           goto fail;
156         }
157       break;
158     }
159
160   if (! mem)
161     {
162       grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory");
163       goto fail;
164     }
165
166   grub_free (mmap);
167   return mem;
168
169  fail:
170   grub_free (mmap);
171   return NULL;
172 }
173
174 grub_err_t
175 grub_efi_prepare_platform (void)
176 {
177   grub_efi_uintn_t mmap_size;
178   grub_efi_uintn_t map_key;
179   grub_efi_uintn_t desc_size;
180   grub_efi_uint32_t desc_version;
181   grub_efi_memory_descriptor_t *mmap_buf;
182   grub_err_t err;
183
184   /*
185    * Cloned from IA64
186    * Must be done after grub_machine_fini because map_key is used by
187    *exit_boot_services.
188    */
189   mmap_size = find_mmap_size ();
190   if (! mmap_size)
191     return GRUB_ERR_OUT_OF_MEMORY;
192   mmap_buf = grub_efi_allocate_any_pages (page_align (mmap_size) >> 12);
193   if (! mmap_buf)
194     return GRUB_ERR_OUT_OF_MEMORY;
195
196   err = grub_efi_finish_boot_services (&mmap_size, mmap_buf, &map_key,
197                                        &desc_size, &desc_version);
198   if (err != GRUB_ERR_NONE)
199     return err;
200
201   return GRUB_ERR_NONE;
202 }