Support relocating coreboot generated acpi rsdp and pir tables.
[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 #define RSDP_SIGNATURE 0x2052545020445352LL // "RSD PTR "
41
42 static void
43 copy_acpi_rsdp(void *pos)
44 {
45     if (*(u64*)pos != RSDP_SIGNATURE)
46         return;
47     struct rsdp_descriptor *p = pos;
48     u32 length = 20;
49     if (checksum(pos, length) != 0)
50         return;
51     if (p->revision > 1) {
52         length = p->length;
53         if (checksum(pos, length) != 0)
54             return;
55     }
56     bios_table_cur_addr = ALIGN(bios_table_cur_addr, 16);
57     if (bios_table_cur_addr + length > bios_table_end_addr) {
58         dprintf(1, "No room to copy ACPI RSDP table!\n");
59         return;
60     }
61     dprintf(1, "Copying ACPI RSDP from %p to %x\n", pos, bios_table_cur_addr);
62     memcpy((void*)bios_table_cur_addr, pos, length);
63     bios_table_cur_addr += length;
64 }
65
66 // Attempt to find (and relocate) any standard bios tables found in a
67 // given address range.
68 void
69 scan_tables(u32 start, u32 size)
70 {
71     void *p = (void*)ALIGN(start, 16);
72     void *end = (void*)start + size;
73     for (; p<end; p += 16) {
74         copy_pir(p);
75         copy_acpi_rsdp(p);
76     }
77 }
78
79
80 /****************************************************************
81  * Memory map
82  ****************************************************************/
83
84 struct cb_header {
85     u32 signature;
86     u32 header_bytes;
87     u32 header_checksum;
88     u32 table_bytes;
89     u32 table_checksum;
90     u32 table_entries;
91 };
92
93 #define CB_SIGNATURE 0x4f49424C // "LBIO"
94
95 struct cb_memory_range {
96     u64 start;
97     u64 size;
98     u32 type;
99 };
100
101 #define CB_MEM_TABLE    16
102
103 struct cb_memory {
104     u32 tag;
105     u32 size;
106     struct cb_memory_range map[0];
107 };
108
109 #define CB_TAG_MEMORY 0x01
110
111 #define MEM_RANGE_COUNT(_rec) \
112         (((_rec)->size - sizeof(*(_rec))) / sizeof((_rec)->map[0]))
113
114 static u16
115 ipchksum(char *buf, int count)
116 {
117     u16 *p = (u16*)buf;
118     u32 sum = 0;
119     while (count > 1) {
120         sum += *p++;
121         count -= 2;
122     }
123     if (count)
124         sum += *(u8*)p;
125     sum = (sum >> 16) + (sum & 0xffff);
126     sum += (sum >> 16);
127     return ~sum;
128 }
129
130 // Try to locate the coreboot header in a given address range.
131 static struct cb_header *
132 find_cb_header(char *addr, int len)
133 {
134     char *end = addr + len;
135     for (; addr < end; addr += 16) {
136         struct cb_header *cbh = (struct cb_header *)addr;
137         if (cbh->signature != CB_SIGNATURE)
138             continue;
139         if (! cbh->table_bytes)
140             continue;
141         if (ipchksum(addr, sizeof(*cbh)) != 0)
142             continue;
143         if (ipchksum(addr + sizeof(*cbh), cbh->table_bytes)
144             != cbh->table_checksum)
145             continue;
146         return cbh;
147     }
148     return NULL;
149 }
150
151 // Try to find the coreboot memory table in the given coreboot table.
152 static void *
153 find_cb_subtable(struct cb_header *cbh, u32 tag)
154 {
155     char *tbl = (char *)cbh + sizeof(*cbh);
156     int i;
157     for (i=0; i<cbh->table_entries; i++) {
158         struct cb_memory *cbm = (struct cb_memory *)tbl;
159         tbl += cbm->size;
160         if (cbm->tag == tag)
161             return cbm;
162     }
163     return NULL;
164 }
165
166 // Populate max ram and e820 map info by scanning for a coreboot table.
167 void
168 coreboot_fill_map()
169 {
170     dprintf(3, "Attempting to find coreboot table\n");
171     struct cb_header *cbh = find_cb_header(0, 0x1000);
172     if (!cbh)
173         goto fail;
174     struct cb_memory *cbm = find_cb_subtable(cbh, CB_TAG_MEMORY);
175     if (!cbm)
176         goto fail;
177
178     u64 maxram = 0;
179     int i, count = MEM_RANGE_COUNT(cbm);
180     for (i=0; i<count; i++) {
181         struct cb_memory_range *m = &cbm->map[i];
182         u32 type = m->type;
183         if (type == CB_MEM_TABLE) {
184             type = E820_RESERVED;
185             scan_tables(m->start, m->size);
186         }
187         if ((type == E820_ACPI || type == E820_RAM)
188             && (m->start + m->size) > maxram)
189             maxram = m->start + m->size;
190         add_e820(m->start, m->size, type);
191     }
192
193     // Ughh - coreboot likes to set a map at 0x0000-0x1000, but this
194     // confuses grub.  So, override it.
195     add_e820(0, 16*1024, E820_RAM);
196
197     SET_EBDA(ram_size, maxram);
198     return;
199
200 fail:
201     // No table found..  Use 16Megs as a dummy value.
202     dprintf(1, "Unable to find coreboot table!\n");
203     SET_EBDA(ram_size, 16*1024*1024);
204     add_e820(0, 16*1024*1024, E820_RAM);
205     return;
206 }