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