Scan for and relocate mptable when using coreboot.
[seabios.git] / src / coreboot.c
1 // Coreboot interface support.
2 //
3 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
4 //
5 // This file may be distributed under the terms of the GNU GPLv3 license.
6
7 #include "memmap.h" // add_e820
8 #include "util.h" // dprintf
9 #include "pci.h" // struct pir_header
10 #include "acpi.h" // struct rsdp_descriptor
11
12
13 /****************************************************************
14  * BIOS table copying
15  ****************************************************************/
16
17 static void
18 copy_pir(void *pos)
19 {
20     struct pir_header *p = pos;
21     if (p->signature != PIR_SIGNATURE)
22         return;
23     if (GET_EBDA(pir_loc))
24         return;
25     if (p->size < sizeof(*p))
26         return;
27     if (checksum(pos, p->size) != 0)
28         return;
29     bios_table_cur_addr = ALIGN(bios_table_cur_addr, 16);
30     if (bios_table_cur_addr + p->size > bios_table_end_addr) {
31         dprintf(1, "No room to copy PIR table!\n");
32         return;
33     }
34     dprintf(1, "Copying PIR from %p to %x\n", pos, bios_table_cur_addr);
35     memcpy((void*)bios_table_cur_addr, pos, p->size);
36     SET_EBDA(pir_loc, bios_table_cur_addr);
37     bios_table_cur_addr += p->size;
38 }
39
40 static void
41 copy_mptable(void *pos)
42 {
43     struct mptable_floating_s *p = pos;
44     if (p->signature != MPTABLE_SIGNAURE)
45         return;
46     if (!p->physaddr)
47         return;
48     if (checksum(pos, sizeof(*p)) != 0)
49         return;
50     u32 length = p->length * 16;
51     bios_table_cur_addr = ALIGN(bios_table_cur_addr, 16);
52     if (bios_table_cur_addr + length > bios_table_end_addr) {
53         dprintf(1, "No room to copy MPTABLE!\n");
54         return;
55     }
56     dprintf(1, "Copying MPTABLE from %p to %x\n", pos, bios_table_cur_addr);
57     memcpy((void*)bios_table_cur_addr, pos, length);
58     SET_EBDA(pir_loc, bios_table_cur_addr);
59     bios_table_cur_addr += length;
60 }
61
62 static void
63 copy_acpi_rsdp(void *pos)
64 {
65     if (*(u64*)pos != RSDP_SIGNATURE)
66         return;
67     struct rsdp_descriptor *p = pos;
68     u32 length = 20;
69     if (checksum(pos, length) != 0)
70         return;
71     if (p->revision > 1) {
72         length = p->length;
73         if (checksum(pos, length) != 0)
74             return;
75     }
76     bios_table_cur_addr = ALIGN(bios_table_cur_addr, 16);
77     if (bios_table_cur_addr + length > bios_table_end_addr) {
78         dprintf(1, "No room to copy ACPI RSDP table!\n");
79         return;
80     }
81     dprintf(1, "Copying ACPI RSDP from %p to %x\n", pos, bios_table_cur_addr);
82     memcpy((void*)bios_table_cur_addr, pos, length);
83     bios_table_cur_addr += length;
84 }
85
86 // Attempt to find (and relocate) any standard bios tables found in a
87 // given address range.
88 void
89 scan_tables(u32 start, u32 size)
90 {
91     void *p = (void*)ALIGN(start, 16);
92     void *end = (void*)start + size;
93     for (; p<end; p += 16) {
94         copy_pir(p);
95         copy_mptable(p);
96         copy_acpi_rsdp(p);
97     }
98 }
99
100
101 /****************************************************************
102  * Memory map
103  ****************************************************************/
104
105 struct cb_header {
106     u32 signature;
107     u32 header_bytes;
108     u32 header_checksum;
109     u32 table_bytes;
110     u32 table_checksum;
111     u32 table_entries;
112 };
113
114 #define CB_SIGNATURE 0x4f49424C // "LBIO"
115
116 struct cb_memory_range {
117     u64 start;
118     u64 size;
119     u32 type;
120 };
121
122 #define CB_MEM_TABLE    16
123
124 struct cb_memory {
125     u32 tag;
126     u32 size;
127     struct cb_memory_range map[0];
128 };
129
130 #define CB_TAG_MEMORY 0x01
131
132 #define MEM_RANGE_COUNT(_rec) \
133         (((_rec)->size - sizeof(*(_rec))) / sizeof((_rec)->map[0]))
134
135 static u16
136 ipchksum(char *buf, int count)
137 {
138     u16 *p = (u16*)buf;
139     u32 sum = 0;
140     while (count > 1) {
141         sum += *p++;
142         count -= 2;
143     }
144     if (count)
145         sum += *(u8*)p;
146     sum = (sum >> 16) + (sum & 0xffff);
147     sum += (sum >> 16);
148     return ~sum;
149 }
150
151 // Try to locate the coreboot header in a given address range.
152 static struct cb_header *
153 find_cb_header(char *addr, int len)
154 {
155     char *end = addr + len;
156     for (; addr < end; addr += 16) {
157         struct cb_header *cbh = (struct cb_header *)addr;
158         if (cbh->signature != CB_SIGNATURE)
159             continue;
160         if (! cbh->table_bytes)
161             continue;
162         if (ipchksum(addr, sizeof(*cbh)) != 0)
163             continue;
164         if (ipchksum(addr + sizeof(*cbh), cbh->table_bytes)
165             != cbh->table_checksum)
166             continue;
167         return cbh;
168     }
169     return NULL;
170 }
171
172 // Try to find the coreboot memory table in the given coreboot table.
173 static void *
174 find_cb_subtable(struct cb_header *cbh, u32 tag)
175 {
176     char *tbl = (char *)cbh + sizeof(*cbh);
177     int i;
178     for (i=0; i<cbh->table_entries; i++) {
179         struct cb_memory *cbm = (struct cb_memory *)tbl;
180         tbl += cbm->size;
181         if (cbm->tag == tag)
182             return cbm;
183     }
184     return NULL;
185 }
186
187 // Populate max ram and e820 map info by scanning for a coreboot table.
188 void
189 coreboot_fill_map()
190 {
191     dprintf(3, "Attempting to find coreboot table\n");
192     struct cb_header *cbh = find_cb_header(0, 0x1000);
193     if (!cbh)
194         goto fail;
195     struct cb_memory *cbm = find_cb_subtable(cbh, CB_TAG_MEMORY);
196     if (!cbm)
197         goto fail;
198
199     u64 maxram = 0;
200     int i, count = MEM_RANGE_COUNT(cbm);
201     for (i=0; i<count; i++) {
202         struct cb_memory_range *m = &cbm->map[i];
203         u32 type = m->type;
204         if (type == CB_MEM_TABLE) {
205             type = E820_RESERVED;
206             scan_tables(m->start, m->size);
207         }
208         if ((type == E820_ACPI || type == E820_RAM)
209             && (m->start + m->size) > maxram)
210             maxram = m->start + m->size;
211         add_e820(m->start, m->size, type);
212     }
213
214     // Ughh - coreboot likes to set a map at 0x0000-0x1000, but this
215     // confuses grub.  So, override it.
216     add_e820(0, 16*1024, E820_RAM);
217
218     SET_EBDA(ram_size, maxram);
219     return;
220
221 fail:
222     // No table found..  Use 16Megs as a dummy value.
223     dprintf(1, "Unable to find coreboot table!\n");
224     SET_EBDA(ram_size, 16*1024*1024);
225     add_e820(0, 16*1024*1024, E820_RAM);
226     return;
227 }