x86: Initial import from Intel FSP release for Queensbay platform
[platform/kernel/u-boot.git] / arch / x86 / cpu / queensbay / fsp_support.c
1 /*
2  * Copyright (C) 2013, Intel Corporation
3  * Copyright (C) 2014, Bin Meng <bmeng.cn@gmail.com>
4  *
5  * SPDX-License-Identifier:     Intel
6  */
7
8 #include <types.h>
9 #include <string.h>
10 #include "fsp_support.h"
11
12 /**
13  * Reads a 64-bit value from memory that may be unaligned.
14  *
15  * This function returns the 64-bit value pointed to by buf. The function
16  * guarantees that the read operation does not produce an alignment fault.
17  *
18  * If the buf is NULL, then ASSERT().
19  *
20  * @buf: Pointer to a 64-bit value that may be unaligned.
21  *
22  * @return: The 64-bit value read from buf.
23  */
24 static u64 read_unaligned64(const u64 *buf)
25 {
26         ASSERT(buf != NULL);
27
28         return *buf;
29 }
30
31 /**
32  * Compares two GUIDs
33  *
34  * If the GUIDs are identical then TRUE is returned.
35  * If there are any bit differences in the two GUIDs, then FALSE is returned.
36  *
37  * If guid1 is NULL, then ASSERT().
38  * If guid2 is NULL, then ASSERT().
39  *
40  * @guid1:        A pointer to a 128 bit GUID.
41  * @guid2:        A pointer to a 128 bit GUID.
42  *
43  * @retval TRUE:  guid1 and guid2 are identical.
44  * @retval FALSE: guid1 and guid2 are not identical.
45  */
46 static unsigned char compare_guid(const struct efi_guid_t *guid1,
47                                   const struct efi_guid_t *guid2)
48 {
49         u64 guid1_low;
50         u64 guid2_low;
51         u64 guid1_high;
52         u64 guid2_high;
53
54         guid1_low  = read_unaligned64((const u64 *)guid1);
55         guid2_low  = read_unaligned64((const u64 *)guid2);
56         guid1_high = read_unaligned64((const u64 *)guid1 + 1);
57         guid2_high = read_unaligned64((const u64 *)guid2 + 1);
58
59         return (unsigned char)(guid1_low == guid2_low && guid1_high == guid2_high);
60 }
61
62 u32 __attribute__((optimize("O0"))) find_fsp_header(void)
63 {
64         volatile register u8 *fsp asm("eax");
65
66         /* Initalize the FSP base */
67         fsp = (u8 *)CONFIG_FSP_LOCATION;
68
69         /* Check the FV signature, _FVH */
70         if (((struct fv_header_t *)fsp)->sign == 0x4856465F) {
71                 /* Go to the end of the FV header and align the address */
72                 fsp += ((struct fv_header_t *)fsp)->ext_hdr_off;
73                 fsp += ((struct fv_ext_header_t *)fsp)->ext_hdr_size;
74                 fsp  = (u8 *)(((u32)fsp + 7) & 0xFFFFFFF8);
75         } else {
76                 fsp  = 0;
77         }
78
79         /* Check the FFS GUID */
80         if (fsp &&
81             (((u32 *)&(((struct ffs_file_header_t *)fsp)->name))[0] == 0x912740BE) &&
82             (((u32 *)&(((struct ffs_file_header_t *)fsp)->name))[1] == 0x47342284) &&
83             (((u32 *)&(((struct ffs_file_header_t *)fsp)->name))[2] == 0xB08471B9) &&
84             (((u32 *)&(((struct ffs_file_header_t *)fsp)->name))[3] == 0x0C3F3527)) {
85                 /* Add the FFS header size to find the raw section header */
86                 fsp += sizeof(struct ffs_file_header_t);
87         } else {
88                 fsp = 0;
89         }
90
91         if (fsp &&
92             ((struct raw_section_t *)fsp)->type == EFI_SECTION_RAW) {
93                 /* Add the raw section header size to find the FSP header */
94                 fsp += sizeof(struct raw_section_t);
95         } else {
96                 fsp = 0;
97         }
98
99         return (u32)fsp;
100 }
101
102 #ifdef __PRE_RAM__
103 void fsp_continue(struct shared_data_t *shared_data, u32 status, void *hob_list)
104 {
105         u32 stack_len;
106         u32 stack_base;
107         u32 stack_top;
108
109         ASSERT(status == 0);
110
111         /* Get the migrated stack in normal memory */
112         stack_base = (u32)get_bootloader_tmp_mem(hob_list, &stack_len);
113         ASSERT(stack_base != 0);
114         stack_top  = stack_base + stack_len - sizeof(u32);
115
116         /*
117          * Old stack base is stored at the very end of the stack top,
118          * use it to calculate the migrated shared data base
119          */
120         shared_data = (struct shared_data_t *)(stack_base +
121                         ((u32)shared_data - *(u32 *)stack_top));
122
123         /* The boot loader main function entry */
124         bl_main_continue(hob_list, shared_data);
125 }
126
127 void fsp_init(u32 stack_top, u32 boot_mode, void *nvs_buf)
128 {
129         struct shared_data_t shared_data;
130         fsp_init_f init;
131         struct fsp_init_params_t params;
132         struct fspinit_rtbuf_t rt_buf;
133         struct vpd_region_t *fsp_vpd;
134         struct fsp_header_t *fsp_hdr;
135         struct fsp_init_params_t *params_ptr;
136         struct upd_region_t *fsp_upd;
137
138         fsp_hdr = (struct fsp_header_t *)find_fsp_header();
139         if (fsp_hdr == NULL) {
140                 /* No valid FSP info header was found */
141                 ASSERT(FALSE);
142         }
143
144         fsp_upd = (struct upd_region_t *)&shared_data.fsp_upd;
145         memset((void *)&rt_buf, 0, sizeof(struct fspinit_rtbuf_t));
146
147         /* Reserve a gap in stack top */
148         rt_buf.common.stack_top = (u32 *)stack_top - 32;
149         rt_buf.common.boot_mode = boot_mode;
150         rt_buf.common.upd_data = (struct upd_region_t *)fsp_upd;
151
152         /* Get VPD region start */
153         fsp_vpd = (struct vpd_region_t *)(fsp_hdr->img_base +
154                         fsp_hdr->cfg_region_off);
155
156         /* Verifify the VPD data region is valid */
157         ASSERT((fsp_vpd->img_rev == VPD_IMAGE_REV) &&
158                (fsp_vpd->sign == VPD_IMAGE_ID));
159
160         /* Copy default data from Flash */
161         memcpy(fsp_upd, (void *)(fsp_hdr->img_base + fsp_vpd->upd_offset),
162                sizeof(struct upd_region_t));
163
164         /* Verifify the UPD data region is valid */
165         ASSERT(fsp_upd->terminator == 0x55AA);
166
167         /* Override any UPD setting if required */
168         update_fsp_upd(fsp_upd);
169
170         memset((void *)&params, 0, sizeof(struct fsp_init_params_t));
171         params.nvs_buf = nvs_buf;
172         params.rt_buf = (struct fspinit_rtbuf_t *)&rt_buf;
173         params.continuation = (fsp_continuation_f)asm_continuation;
174
175         init = (fsp_init_f)(fsp_hdr->img_base + fsp_hdr->fsp_init);
176         params_ptr = &params;
177
178         shared_data.fsp_hdr = fsp_hdr;
179         shared_data.stack_top = (u32 *)stack_top;
180
181         /*
182          * Use ASM code to ensure the register value in EAX & ECX
183          * will be passed into BlContinuationFunc
184          */
185         asm volatile (
186                 "pushl  %0;"
187                 "call   *%%eax;"
188                 ".global asm_continuation;"
189                 "asm_continuation:;"
190                 "popl   %%eax;" /* pop out return address */
191                 "pushl  %%ecx;" /* push shared_data pointer */
192                 "pushl  %%eax;" /* push back return address */
193                 "jmp    fsp_continue;"
194                 : : "m"(params_ptr), "a"(init), "c"(&shared_data)
195         );
196
197         /*
198          * Should never get here.
199          * Control will continue from romstage_main_continue_asm.
200          * This line below is to prevent the compiler from optimizing
201          * structure intialization.
202          */
203         init(&params);
204
205         /*
206          * Should never return.
207          * Control will continue from ContinuationFunc
208          */
209         ASSERT(FALSE);
210 }
211
212 #else
213
214 u32 fsp_notify(struct fsp_header_t *fsp_hdr, u32 phase)
215 {
216         fsp_notify_f notify;
217         struct fsp_notify_params_t params;
218         u32 status;
219
220         if (!fsp_hdr)
221                 fsp_hdr = (struct fsp_header_t *)find_fsp_header();
222
223         if (fsp_hdr == NULL) {
224                 /* No valid FSP info header */
225                 ASSERT(FALSE);
226         }
227
228         notify = (fsp_notify_f)(fsp_hdr->img_base + fsp_hdr->fsp_notify);
229         params.phase = phase;
230         status = notify(&params);
231
232         return status;
233 }
234
235 #endif  /* __PRE_RAM__ */
236
237 u32 get_usable_lowmem_top(const void *hob_list)
238 {
239         union hob_pointers_t hob;
240         phys_addr_t phys_start;
241         u32 top;
242
243         /* Get the HOB list for processing */
244         hob.raw = (void *)hob_list;
245
246         /* * Collect memory ranges */
247         top = 0x100000;
248         while (!END_OF_HOB(hob)) {
249                 if (hob.hdr->type == HOB_TYPE_RES_DESC) {
250                         if (hob.res_desc->type == RES_SYS_MEM) {
251                                 phys_start = hob.res_desc->phys_start;
252                                 /* Need memory above 1MB to be collected here */
253                                 if (phys_start >= 0x100000 &&
254                                     phys_start < (phys_addr_t)0x100000000)
255                                         top += (u32)(hob.res_desc->len);
256                         }
257                 }
258                 hob.raw = GET_NEXT_HOB(hob);
259         }
260
261         return top;
262 }
263
264 u64 get_usable_highmem_top(const void *hob_list)
265 {
266         union hob_pointers_t hob;
267         phys_addr_t phys_start;
268         u64 top;
269
270         /* Get the HOB list for processing */
271         hob.raw = (void *)hob_list;
272
273         /* Collect memory ranges */
274         top = 0x100000000;
275         while (!END_OF_HOB(hob)) {
276                 if (hob.hdr->type == HOB_TYPE_RES_DESC) {
277                         if (hob.res_desc->type == RES_SYS_MEM) {
278                                 phys_start = hob.res_desc->phys_start;
279                                 /* Need memory above 1MB to be collected here */
280                                 if (phys_start >= (phys_addr_t)0x100000000)
281                                         top += (u32)(hob.res_desc->len);
282                         }
283                 }
284                 hob.raw = GET_NEXT_HOB(hob);
285         }
286
287         return top;
288 }
289
290 u64 get_fsp_reserved_mem_from_guid(const void *hob_list, u64 *len,
291                                    struct efi_guid_t *guid)
292 {
293         union hob_pointers_t hob;
294
295         /* Get the HOB list for processing */
296         hob.raw = (void *)hob_list;
297
298         /* Collect memory ranges */
299         while (!END_OF_HOB(hob)) {
300                 if (hob.hdr->type == HOB_TYPE_RES_DESC) {
301                         if (hob.res_desc->type == RES_MEM_RESERVED) {
302                                 if (compare_guid(&hob.res_desc->owner, guid)) {
303                                         if (len)
304                                                 *len = (u32)(hob.res_desc->len);
305
306                                         return (u64)(hob.res_desc->phys_start);
307                                 }
308                         }
309                 }
310                 hob.raw = GET_NEXT_HOB(hob);
311         }
312
313         return 0;
314 }
315
316 u32 get_fsp_reserved_mem(const void *hob_list, u32 *len)
317 {
318         const struct efi_guid_t guid = FSP_HOB_RESOURCE_OWNER_FSP_GUID;
319         u64 length;
320         u32 base;
321
322         base = (u32)get_fsp_reserved_mem_from_guid(hob_list,
323                         &length, (struct efi_guid_t *)&guid);
324         if ((len != 0) && (base != 0))
325                 *len = (u32)length;
326
327         return base;
328 }
329
330 u32 get_tseg_reserved_mem(const void *hob_list, u32 *len)
331 {
332         const struct efi_guid_t guid = FSP_HOB_RESOURCE_OWNER_TSEG_GUID;
333         u64 length;
334         u32 base;
335
336         base = (u32)get_fsp_reserved_mem_from_guid(hob_list,
337                         &length, (struct efi_guid_t *)&guid);
338         if ((len != 0) && (base != 0))
339                 *len = (u32)length;
340
341         return base;
342 }
343
344 void *get_next_hob(u16 type, const void *hob_list)
345 {
346         union hob_pointers_t hob;
347
348         ASSERT(hob_list != NULL);
349
350         hob.raw = (u8 *)hob_list;
351
352         /* Parse the HOB list until end of list or matching type is found */
353         while (!END_OF_HOB(hob)) {
354                 if (hob.hdr->type == type)
355                         return hob.raw;
356
357                 hob.raw = GET_NEXT_HOB(hob);
358         }
359
360         return NULL;
361 }
362
363 void *get_next_guid_hob(const struct efi_guid_t *guid, const void *hob_list)
364 {
365         union hob_pointers_t hob;
366
367         hob.raw = (u8 *)hob_list;
368         while ((hob.raw = get_next_hob(HOB_TYPE_GUID_EXT,
369                         hob.raw)) != NULL) {
370                 if (compare_guid(guid, &hob.guid->name))
371                         break;
372                 hob.raw = GET_NEXT_HOB(hob);
373         }
374
375         return hob.raw;
376 }
377
378 void *get_guid_hob_data(const void *hob_list, u32 *len, struct efi_guid_t *guid)
379 {
380         u8 *guid_hob;
381
382         guid_hob = get_next_guid_hob(guid, hob_list);
383         if (guid_hob == NULL) {
384                 return NULL;
385         } else {
386                 if (len)
387                         *len = GET_GUID_HOB_DATA_SIZE(guid_hob);
388
389                 return GET_GUID_HOB_DATA(guid_hob);
390         }
391 }
392
393 void *get_fsp_nvs_data(const void *hob_list, u32 *len)
394 {
395         const struct efi_guid_t guid = FSP_NON_VOLATILE_STORAGE_HOB_GUID;
396
397         return get_guid_hob_data(hob_list, len, (struct efi_guid_t *)&guid);
398 }
399
400 void *get_bootloader_tmp_mem(const void *hob_list, u32 *len)
401 {
402         const struct efi_guid_t guid = FSP_BOOTLOADER_TEMP_MEM_HOB_GUID;
403
404         return get_guid_hob_data(hob_list, len, (struct efi_guid_t *)&guid);
405 }