xref: /qemu/hw/i386/pc_sysfw_ovmf.c (revision fc524567087c2537b5103cdfc1d41e4f442892b6)
1b5b31860SPhilippe Mathieu-Daudé /*
2b5b31860SPhilippe Mathieu-Daudé  * QEMU PC System Firmware (OVMF specific)
3b5b31860SPhilippe Mathieu-Daudé  *
4b5b31860SPhilippe Mathieu-Daudé  * Copyright (c) 2003-2004 Fabrice Bellard
5b5b31860SPhilippe Mathieu-Daudé  * Copyright (c) 2011-2012 Intel Corporation
6b5b31860SPhilippe Mathieu-Daudé  *
7b5b31860SPhilippe Mathieu-Daudé  * Permission is hereby granted, free of charge, to any person obtaining a copy
8b5b31860SPhilippe Mathieu-Daudé  * of this software and associated documentation files (the "Software"), to deal
9b5b31860SPhilippe Mathieu-Daudé  * in the Software without restriction, including without limitation the rights
10b5b31860SPhilippe Mathieu-Daudé  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11b5b31860SPhilippe Mathieu-Daudé  * copies of the Software, and to permit persons to whom the Software is
12b5b31860SPhilippe Mathieu-Daudé  * furnished to do so, subject to the following conditions:
13b5b31860SPhilippe Mathieu-Daudé  *
14b5b31860SPhilippe Mathieu-Daudé  * The above copyright notice and this permission notice shall be included in
15b5b31860SPhilippe Mathieu-Daudé  * all copies or substantial portions of the Software.
16b5b31860SPhilippe Mathieu-Daudé  *
17b5b31860SPhilippe Mathieu-Daudé  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18b5b31860SPhilippe Mathieu-Daudé  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19b5b31860SPhilippe Mathieu-Daudé  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20b5b31860SPhilippe Mathieu-Daudé  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21b5b31860SPhilippe Mathieu-Daudé  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22b5b31860SPhilippe Mathieu-Daudé  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23b5b31860SPhilippe Mathieu-Daudé  * THE SOFTWARE.
24b5b31860SPhilippe Mathieu-Daudé  */
25b5b31860SPhilippe Mathieu-Daudé 
26b5b31860SPhilippe Mathieu-Daudé #include "qemu/osdep.h"
2764915058SDov Murik #include "qemu/error-report.h"
28b5b31860SPhilippe Mathieu-Daudé #include "hw/i386/pc.h"
29*9c2ff9cdSPierrick Bouvier #include "exec/target_page.h"
30b5b31860SPhilippe Mathieu-Daudé #include "cpu.h"
31b5b31860SPhilippe Mathieu-Daudé 
32b5b31860SPhilippe Mathieu-Daudé #define OVMF_TABLE_FOOTER_GUID "96b582de-1fb2-45f7-baea-a366c55a082d"
33b5b31860SPhilippe Mathieu-Daudé 
34bfc8c144SDov Murik static const int bytes_after_table_footer = 32;
35bfc8c144SDov Murik 
36b5b31860SPhilippe Mathieu-Daudé static bool ovmf_flash_parsed;
37b5b31860SPhilippe Mathieu-Daudé static uint8_t *ovmf_table;
38b5b31860SPhilippe Mathieu-Daudé static int ovmf_table_len;
39b5b31860SPhilippe Mathieu-Daudé 
pc_system_parse_ovmf_flash(uint8_t * flash_ptr,size_t flash_size)40b5b31860SPhilippe Mathieu-Daudé void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size)
41b5b31860SPhilippe Mathieu-Daudé {
42b5b31860SPhilippe Mathieu-Daudé     uint8_t *ptr;
43b5b31860SPhilippe Mathieu-Daudé     QemuUUID guid;
44b5b31860SPhilippe Mathieu-Daudé     int tot_len;
45b5b31860SPhilippe Mathieu-Daudé 
46b5b31860SPhilippe Mathieu-Daudé     /* should only be called once */
47b5b31860SPhilippe Mathieu-Daudé     if (ovmf_flash_parsed) {
48b5b31860SPhilippe Mathieu-Daudé         return;
49b5b31860SPhilippe Mathieu-Daudé     }
50b5b31860SPhilippe Mathieu-Daudé 
51b5b31860SPhilippe Mathieu-Daudé     ovmf_flash_parsed = true;
52b5b31860SPhilippe Mathieu-Daudé 
53b5b31860SPhilippe Mathieu-Daudé     if (flash_size < TARGET_PAGE_SIZE) {
54b5b31860SPhilippe Mathieu-Daudé         return;
55b5b31860SPhilippe Mathieu-Daudé     }
56b5b31860SPhilippe Mathieu-Daudé 
57b5b31860SPhilippe Mathieu-Daudé     /*
58b5b31860SPhilippe Mathieu-Daudé      * if this is OVMF there will be a table footer
59bfc8c144SDov Murik      * guid 48 bytes before the end of the flash file
60bfc8c144SDov Murik      * (= 32 bytes after the table + 16 bytes the GUID itself).
61bfc8c144SDov Murik      * If it's not found, silently abort the flash parsing.
62b5b31860SPhilippe Mathieu-Daudé      */
63b5b31860SPhilippe Mathieu-Daudé     qemu_uuid_parse(OVMF_TABLE_FOOTER_GUID, &guid);
64b5b31860SPhilippe Mathieu-Daudé     guid = qemu_uuid_bswap(guid); /* guids are LE */
65bfc8c144SDov Murik     ptr = flash_ptr + flash_size - (bytes_after_table_footer + sizeof(guid));
66b5b31860SPhilippe Mathieu-Daudé     if (!qemu_uuid_is_equal((QemuUUID *)ptr, &guid)) {
67b5b31860SPhilippe Mathieu-Daudé         return;
68b5b31860SPhilippe Mathieu-Daudé     }
69b5b31860SPhilippe Mathieu-Daudé 
70b5b31860SPhilippe Mathieu-Daudé     /* if found, just before is two byte table length */
71b5b31860SPhilippe Mathieu-Daudé     ptr -= sizeof(uint16_t);
72b5b31860SPhilippe Mathieu-Daudé     tot_len = le16_to_cpu(*(uint16_t *)ptr) - sizeof(guid) - sizeof(uint16_t);
73b5b31860SPhilippe Mathieu-Daudé 
7464915058SDov Murik     if (tot_len < 0 || tot_len > (ptr - flash_ptr)) {
7564915058SDov Murik         error_report("OVMF table has invalid size %d", tot_len);
7664915058SDov Murik         return;
7764915058SDov Murik     }
7864915058SDov Murik 
7964915058SDov Murik     if (tot_len == 0) {
8064915058SDov Murik         /* no entries in the OVMF table */
81b5b31860SPhilippe Mathieu-Daudé         return;
82b5b31860SPhilippe Mathieu-Daudé     }
83b5b31860SPhilippe Mathieu-Daudé 
84b5b31860SPhilippe Mathieu-Daudé     ovmf_table = g_malloc(tot_len);
85b5b31860SPhilippe Mathieu-Daudé     ovmf_table_len = tot_len;
86b5b31860SPhilippe Mathieu-Daudé 
87b5b31860SPhilippe Mathieu-Daudé     /*
88b5b31860SPhilippe Mathieu-Daudé      * ptr is the foot of the table, so copy it all to the newly
89b5b31860SPhilippe Mathieu-Daudé      * allocated ovmf_table and then set the ovmf_table pointer
90b5b31860SPhilippe Mathieu-Daudé      * to the table foot
91b5b31860SPhilippe Mathieu-Daudé      */
92b5b31860SPhilippe Mathieu-Daudé     memcpy(ovmf_table, ptr - tot_len, tot_len);
93b5b31860SPhilippe Mathieu-Daudé     ovmf_table += tot_len;
94b5b31860SPhilippe Mathieu-Daudé }
95b5b31860SPhilippe Mathieu-Daudé 
96b5b31860SPhilippe Mathieu-Daudé /**
97b5b31860SPhilippe Mathieu-Daudé  * pc_system_ovmf_table_find - Find the data associated with an entry in OVMF's
98b5b31860SPhilippe Mathieu-Daudé  * reset vector GUIDed table.
99b5b31860SPhilippe Mathieu-Daudé  *
100b5b31860SPhilippe Mathieu-Daudé  * @entry: GUID string of the entry to lookup
101b5b31860SPhilippe Mathieu-Daudé  * @data: Filled with a pointer to the entry's value (if not NULL)
102b5b31860SPhilippe Mathieu-Daudé  * @data_len: Filled with the length of the entry's value (if not NULL). Pass
103b5b31860SPhilippe Mathieu-Daudé  *            NULL here if the length of data is known.
104b5b31860SPhilippe Mathieu-Daudé  *
105b5b31860SPhilippe Mathieu-Daudé  * Return: true if the entry was found in the OVMF table; false otherwise.
106b5b31860SPhilippe Mathieu-Daudé  */
pc_system_ovmf_table_find(const char * entry,uint8_t ** data,int * data_len)107b5b31860SPhilippe Mathieu-Daudé bool pc_system_ovmf_table_find(const char *entry, uint8_t **data,
108b5b31860SPhilippe Mathieu-Daudé                                int *data_len)
109b5b31860SPhilippe Mathieu-Daudé {
110b5b31860SPhilippe Mathieu-Daudé     uint8_t *ptr = ovmf_table;
111b5b31860SPhilippe Mathieu-Daudé     int tot_len = ovmf_table_len;
112b5b31860SPhilippe Mathieu-Daudé     QemuUUID entry_guid;
113b5b31860SPhilippe Mathieu-Daudé 
114b5b31860SPhilippe Mathieu-Daudé     assert(ovmf_flash_parsed);
115b5b31860SPhilippe Mathieu-Daudé 
116b5b31860SPhilippe Mathieu-Daudé     if (qemu_uuid_parse(entry, &entry_guid) < 0) {
117b5b31860SPhilippe Mathieu-Daudé         return false;
118b5b31860SPhilippe Mathieu-Daudé     }
119b5b31860SPhilippe Mathieu-Daudé 
120b5b31860SPhilippe Mathieu-Daudé     if (!ptr) {
121b5b31860SPhilippe Mathieu-Daudé         return false;
122b5b31860SPhilippe Mathieu-Daudé     }
123b5b31860SPhilippe Mathieu-Daudé 
124b5b31860SPhilippe Mathieu-Daudé     entry_guid = qemu_uuid_bswap(entry_guid); /* guids are LE */
125b5b31860SPhilippe Mathieu-Daudé     while (tot_len >= sizeof(QemuUUID) + sizeof(uint16_t)) {
126b5b31860SPhilippe Mathieu-Daudé         int len;
127b5b31860SPhilippe Mathieu-Daudé         QemuUUID *guid;
128b5b31860SPhilippe Mathieu-Daudé 
129b5b31860SPhilippe Mathieu-Daudé         /*
130b5b31860SPhilippe Mathieu-Daudé          * The data structure is
131b5b31860SPhilippe Mathieu-Daudé          *   arbitrary length data
132b5b31860SPhilippe Mathieu-Daudé          *   2 byte length of entire entry
133b5b31860SPhilippe Mathieu-Daudé          *   16 byte guid
134b5b31860SPhilippe Mathieu-Daudé          */
135b5b31860SPhilippe Mathieu-Daudé         guid = (QemuUUID *)(ptr - sizeof(QemuUUID));
136b5b31860SPhilippe Mathieu-Daudé         len = le16_to_cpu(*(uint16_t *)(ptr - sizeof(QemuUUID) -
137b5b31860SPhilippe Mathieu-Daudé                                         sizeof(uint16_t)));
138b5b31860SPhilippe Mathieu-Daudé 
139b5b31860SPhilippe Mathieu-Daudé         /*
140b5b31860SPhilippe Mathieu-Daudé          * just in case the table is corrupt, wouldn't want to spin in
141b5b31860SPhilippe Mathieu-Daudé          * the zero case
142b5b31860SPhilippe Mathieu-Daudé          */
143b5b31860SPhilippe Mathieu-Daudé         if (len < sizeof(QemuUUID) + sizeof(uint16_t)) {
144b5b31860SPhilippe Mathieu-Daudé             return false;
145b5b31860SPhilippe Mathieu-Daudé         } else if (len > tot_len) {
146b5b31860SPhilippe Mathieu-Daudé             return false;
147b5b31860SPhilippe Mathieu-Daudé         }
148b5b31860SPhilippe Mathieu-Daudé 
149b5b31860SPhilippe Mathieu-Daudé         ptr -= len;
150b5b31860SPhilippe Mathieu-Daudé         tot_len -= len;
151b5b31860SPhilippe Mathieu-Daudé         if (qemu_uuid_is_equal(guid, &entry_guid)) {
152b5b31860SPhilippe Mathieu-Daudé             if (data) {
153b5b31860SPhilippe Mathieu-Daudé                 *data = ptr;
154b5b31860SPhilippe Mathieu-Daudé             }
155b5b31860SPhilippe Mathieu-Daudé             if (data_len) {
156b5b31860SPhilippe Mathieu-Daudé                 *data_len = len - sizeof(QemuUUID) - sizeof(uint16_t);
157b5b31860SPhilippe Mathieu-Daudé             }
158b5b31860SPhilippe Mathieu-Daudé             return true;
159b5b31860SPhilippe Mathieu-Daudé         }
160b5b31860SPhilippe Mathieu-Daudé     }
161b5b31860SPhilippe Mathieu-Daudé     return false;
162b5b31860SPhilippe Mathieu-Daudé }
163