]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/boot/ia64/efi/efimd.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / boot / ia64 / efi / efimd.c
1 /*-
2  * Copyright (c) 2004, 2006 Marcel Moolenaar
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <stand.h>
31
32 #include <efi.h>
33 #include <efilib.h>
34
35 #include <libia64.h>
36
37 #define EFI_INTEL_FPSWA         \
38     {0xc41b6531,0x97b9,0x11d3,{0x9a,0x29,0x00,0x90,0x27,0x3f,0xc1,0x4d}}
39
40 static EFI_GUID fpswa_guid = EFI_INTEL_FPSWA;
41
42 /* DIG64 Headless Console & Debug Port Table. */
43 #define HCDP_TABLE_GUID         \
44     {0xf951938d,0x620b,0x42ef,{0x82,0x79,0xa8,0x4b,0x79,0x61,0x78,0x98}}
45
46 static EFI_GUID hcdp_guid = HCDP_TABLE_GUID;
47
48 static EFI_MEMORY_DESCRIPTOR *memmap;
49 static UINTN memmapsz;
50 static UINTN mapkey;
51 static UINTN descsz;
52 static UINT32 descver;
53
54 #define IA64_EFI_CHUNK_SIZE     (32 * 1048576)
55 static vm_paddr_t ia64_efi_chunk;
56
57 #define IA64_EFI_PGTBLSZ_MAX    1048576
58 static vm_paddr_t ia64_efi_pgtbl;
59 static vm_size_t ia64_efi_pgtblsz;
60
61 /* Don't allocate memory below the boundary */
62 #define IA64_EFI_ALLOC_BOUNDARY 1048576
63
64 static int
65 ia64_efi_memmap_update(void)
66 {
67         EFI_STATUS status;
68
69         if (memmap != NULL) {
70                 free(memmap);
71                 memmap = NULL;
72         }
73
74         memmapsz = 0;
75         BS->GetMemoryMap(&memmapsz, NULL, &mapkey, &descsz, &descver);
76         if (memmapsz == 0)
77                 return (FALSE);
78         memmap = malloc(memmapsz);
79         if (memmap == NULL)
80                 return (FALSE);
81
82         status = BS->GetMemoryMap(&memmapsz, memmap, &mapkey, &descsz,
83             &descver);
84         if (EFI_ERROR(status)) {
85                 free(memmap);
86                 memmap = NULL;
87                 return (FALSE);
88         }
89
90         return (TRUE);
91 }
92
93 /*
94  * Returns 0 on failure. Successful allocations return an address
95  * larger or equal to IA64_EFI_ALLOC_BOUNDARY.
96  */
97 static vm_paddr_t
98 ia64_efi_alloc(vm_size_t sz)
99 {
100         EFI_PHYSICAL_ADDRESS pa;
101         EFI_MEMORY_DESCRIPTOR *mm;
102         uint8_t *mmiter, *mmiterend;
103         vm_size_t memsz;
104         UINTN npgs;
105         EFI_STATUS status;
106
107         /* We can't allocate less than a page */
108         if (sz < EFI_PAGE_SIZE)
109                 return (0);
110
111         /* The size must be a power of 2. */
112         if (sz & (sz - 1))
113                 return (0);
114
115         if (!ia64_efi_memmap_update())
116                 return (0);
117
118         mmiter = (void *)memmap;
119         mmiterend = mmiter + memmapsz;
120         for (; mmiter < mmiterend; mmiter += descsz) {
121                 mm = (void *)mmiter;
122                 if (mm->Type != EfiConventionalMemory)
123                         continue;
124                 memsz = mm->NumberOfPages * EFI_PAGE_SIZE;
125                 if (mm->PhysicalStart + memsz <= IA64_EFI_ALLOC_BOUNDARY)
126                         continue;
127                 /*
128                  * XXX We really should make sure the memory is local to the
129                  * BSP.
130                  */
131                 pa = (mm->PhysicalStart < IA64_EFI_ALLOC_BOUNDARY) ?
132                     IA64_EFI_ALLOC_BOUNDARY : mm->PhysicalStart;
133                 pa  = (pa + sz - 1) & ~(sz - 1);
134                 if (pa + sz > mm->PhysicalStart + memsz)
135                         continue;
136
137                 npgs = EFI_SIZE_TO_PAGES(sz);
138                 status = BS->AllocatePages(AllocateAddress, EfiLoaderData,
139                     npgs, &pa);
140                 if (!EFI_ERROR(status))
141                         return (pa);
142         }
143
144         printf("%s: unable to allocate %lx bytes\n", __func__, sz);
145         return (0);
146 }
147
148 vm_paddr_t
149 ia64_platform_alloc(vm_offset_t va, vm_size_t sz)
150 {
151         vm_paddr_t pa;
152
153         if (va == 0) {
154                 /* Page table itself. */
155                 if (sz > IA64_EFI_PGTBLSZ_MAX)
156                         return (~0UL);
157                 if (ia64_efi_pgtbl == 0)
158                         ia64_efi_pgtbl = ia64_efi_alloc(IA64_EFI_PGTBLSZ_MAX);
159                 if (ia64_efi_pgtbl != 0)
160                         ia64_efi_pgtblsz = sz;
161                 return (ia64_efi_pgtbl);
162         } else if (va < IA64_PBVM_BASE) {
163                 /* Should not happen. */
164                 return (~0UL);
165         }
166
167         /* Loader virtual memory page. */
168         va -= IA64_PBVM_BASE;
169
170         /* Allocate a big chunk that can be wired with a single PTE. */
171         if (ia64_efi_chunk == 0)
172                 ia64_efi_chunk = ia64_efi_alloc(IA64_EFI_CHUNK_SIZE);
173         if (va < IA64_EFI_CHUNK_SIZE)
174                 return (ia64_efi_chunk + va);
175
176         /* Allocate a page at a time when we go beyond the chunk. */
177         pa = ia64_efi_alloc(sz);
178         return ((pa == 0) ? ~0UL : pa);
179 }
180
181 void
182 ia64_platform_free(vm_offset_t va, vm_paddr_t pa, vm_size_t sz)
183 {
184
185         BS->FreePages(pa, sz >> EFI_PAGE_SHIFT);
186 }
187
188 int
189 ia64_platform_bootinfo(struct bootinfo *bi, struct bootinfo **res)
190 {
191         VOID *fpswa;
192         EFI_HANDLE handle;
193         EFI_STATUS status;
194         UINTN sz;
195
196         bi->bi_systab = (uint64_t)ST;
197         bi->bi_hcdp = (uint64_t)efi_get_table(&hcdp_guid);
198
199         sz = sizeof(EFI_HANDLE);
200         status = BS->LocateHandle(ByProtocol, &fpswa_guid, 0, &sz, &handle);
201         if (status == 0)
202                 status = BS->HandleProtocol(handle, &fpswa_guid, &fpswa);
203         bi->bi_fpswa = (status == 0) ? (uint64_t)fpswa : 0;
204
205         if (!ia64_efi_memmap_update())
206                 return (ENOMEM);
207
208         bi->bi_memmap = (uint64_t)memmap;
209         bi->bi_memmap_size = memmapsz;
210         bi->bi_memdesc_size = descsz;
211         bi->bi_memdesc_version = descver;
212
213         if (IS_LEGACY_KERNEL())
214                 *res = malloc(sizeof(**res));
215
216         return (0);
217 }
218
219 int
220 ia64_platform_enter(const char *kernel)
221 {
222         EFI_STATUS status;
223
224         status = BS->ExitBootServices(IH, mapkey);
225         if (EFI_ERROR(status)) {
226                 printf("%s: ExitBootServices() returned 0x%lx\n", __func__,
227                     (long)status);
228                 return (EINVAL);
229         }
230
231         return (0);
232 }
233
234 COMMAND_SET(pbvm, "pbvm", "show PBVM details", command_pbvm);
235
236 static int
237 command_pbvm(int argc, char *argv[])
238 {
239         uint64_t limit, pg, start;
240         u_int idx;
241
242         printf("Page table @ %p, size %x\n", ia64_pgtbl, ia64_pgtblsz);
243
244         if (ia64_pgtbl == NULL)
245                 return (0);
246
247         limit = ~0;
248         start = ~0;
249         idx = 0;
250         while (ia64_pgtbl[idx] != 0) {
251                 pg = ia64_pgtbl[idx];
252                 if (pg != limit) {      
253                         if (start != ~0)
254                                 printf("%#lx-%#lx\n", start, limit);
255                         start = pg;
256                 }
257                 limit = pg + IA64_PBVM_PAGE_SIZE;
258                 idx++;
259         }
260         if (start != ~0)
261                 printf("%#lx-%#lx\n", start, limit);
262
263         return (0);
264 }