blob: 8882ee5ef8c8c789a6e0655c25bb66af04b98ff2 [file] [log] [blame]
David Woodhouse118469a2013-01-25 19:46:25 -06001// Compatibility Support Module (CSM) for UEFI / EDK-II
2//
3// Copyright © 2013 Intel Corporation
4//
5// This file may be distributed under the terms of the GNU LGPLv3 license.
6
7#include "config.h" // CONFIG_*
8#include "csm.h"
9#include "util.h" // checksum
10#include "bregs.h"
11#include "optionroms.h"
12#include "pci.h"
13#include "memmap.h"
14#include "biosvar.h"
15#include "post.h"
16#include "acpi.h"
17#include "boot.h"
18#include "smbios.h"
19#include "pic.h"
20
Kevin O'Connor89a2f962013-02-18 23:36:03 -050021struct rsdp_descriptor csm_rsdp VARFSEG __aligned(16);
David Woodhouse118469a2013-01-25 19:46:25 -060022
Kevin O'Connor89a2f962013-02-18 23:36:03 -050023EFI_COMPATIBILITY16_TABLE csm_compat_table VARFSEG __aligned(16) = {
David Woodhouse118469a2013-01-25 19:46:25 -060024 .Signature = 0x24454649,
25 .TableChecksum = 0 /* Filled in by checkrom.py */,
26 .TableLength = sizeof(csm_compat_table),
27 .Compatibility16CallSegment = SEG_BIOS,
28 .Compatibility16CallOffset = 0 /* Filled in by checkrom.py */,
29 .OemIdStringPointer = (u32)"SeaBIOS",
30 .AcpiRsdPtrPointer = (u32)&csm_rsdp,
31};
32
33
34EFI_TO_COMPATIBILITY16_INIT_TABLE *csm_init_table;
35EFI_TO_COMPATIBILITY16_BOOT_TABLE *csm_boot_table;
36
David Woodhouse4b1d2be2013-02-10 00:51:56 +000037extern void __csm_return(struct bregs *regs) __noreturn;
38
39static void
40csm_return(struct bregs *regs)
41{
42 dprintf(3, "handle_csm returning AX=%04x\n", regs->ax);
43
44 pic_save_mask();
45 __csm_return(regs);
46}
David Woodhouse118469a2013-01-25 19:46:25 -060047
48static void
49csm_maininit(struct bregs *regs)
50{
51 interface_init();
52 pci_probe_devices();
53
54 csm_compat_table.PnPInstallationCheckSegment = SEG_BIOS;
55 csm_compat_table.PnPInstallationCheckOffset = get_pnp_offset();
56
57 regs->ax = 0;
58
David Woodhouse118469a2013-01-25 19:46:25 -060059 csm_return(regs);
60}
61
62/* Legacy16InitializeYourself */
63static void
64handle_csm_0000(struct bregs *regs)
65{
66 dprintf(3, "Legacy16InitializeYourself table %04x:%04x\n", regs->es,
67 regs->bx);
68
69 csm_init_table = MAKE_FLATPTR(regs->es, regs->bx);
70
71 dprintf(3, "BiosLessThan1MB %08x\n", csm_init_table->BiosLessThan1MB);
72 dprintf(3, "HiPmmMemory %08x\n", csm_init_table->HiPmmMemory);
73 dprintf(3, "HiPmmMemorySize %08x\n", csm_init_table->HiPmmMemorySizeInBytes);
74 dprintf(3, "ReverseThunk %04x:%04x\n", csm_init_table->ReverseThunkCallSegment,
75 csm_init_table->ReverseThunkCallOffset);
76 dprintf(3, "NumE820Entries %08x\n", csm_init_table->NumberE820Entries);
77 dprintf(3, "OsMemoryAbove1M %08x\n", csm_init_table->OsMemoryAbove1Mb);
78 dprintf(3, "ThunkStart %08x\n", csm_init_table->ThunkStart);
79 dprintf(3, "ThunkSize %08x\n", csm_init_table->ThunkSizeInBytes);
80 dprintf(3, "LoPmmMemory %08x\n", csm_init_table->LowPmmMemory);
81 dprintf(3, "LoPmmMemorySize %08x\n", csm_init_table->LowPmmMemorySizeInBytes);
82
83 csm_malloc_preinit(csm_init_table->LowPmmMemory,
84 csm_init_table->LowPmmMemorySizeInBytes,
85 csm_init_table->HiPmmMemory,
86 csm_init_table->HiPmmMemorySizeInBytes);
87 reloc_preinit(csm_maininit, regs);
88}
89
90/* Legacy16UpdateBbs */
91static void
92handle_csm_0001(struct bregs *regs)
93{
David Woodhouse84b7dcb2013-02-14 09:17:17 +000094 if (!CONFIG_BOOT) {
95 regs->ax = 1;
96 return;
97 }
98
David Woodhouse118469a2013-01-25 19:46:25 -060099 dprintf(3, "Legacy16UpdateBbs table %04x:%04x\n", regs->es, regs->bx);
100
101 csm_boot_table = MAKE_FLATPTR(regs->es, regs->bx);
102 dprintf(3, "MajorVersion %04x\n", csm_boot_table->MajorVersion);
103 dprintf(3, "MinorVersion %04x\n", csm_boot_table->MinorVersion);
104 dprintf(3, "AcpiTable %08x\n", csm_boot_table->AcpiTable);
105 dprintf(3, "SmbiosTable %08x\n", csm_boot_table->SmbiosTable);
106 dprintf(3, "SmbiosTableLength %08x\n", csm_boot_table->SmbiosTableLength);
107// dprintf(3, "SioData %08x\n", csm_boot_table->SioData);
108 dprintf(3, "DevicePathType %04x\n", csm_boot_table->DevicePathType);
109 dprintf(3, "PciIrqMask %04x\n", csm_boot_table->PciIrqMask);
110 dprintf(3, "NumberE820Entries %08x\n", csm_boot_table->NumberE820Entries);
111// dprintf(3, "HddInfo %08x\n", csm_boot_table->HddInfo);
112 dprintf(3, "NumberBbsEntries %08x\n", csm_boot_table->NumberBbsEntries);
113 dprintf(3, "BBsTable %08x\n", csm_boot_table->BbsTable);
114 dprintf(3, "SmmTable %08x\n", csm_boot_table->SmmTable);
115 dprintf(3, "OsMemoryAbove1Mb %08x\n", csm_boot_table->OsMemoryAbove1Mb);
116 dprintf(3, "UnconventionalDeviceTable %08x\n", csm_boot_table->UnconventionalDeviceTable);
117
118 regs->ax = 0;
119}
120
121/* PrepareToBoot */
122static void
123handle_csm_0002(struct bregs *regs)
124{
David Woodhouse84b7dcb2013-02-14 09:17:17 +0000125 if (!CONFIG_BOOT) {
126 regs->ax = 1;
127 return;
128 }
129
David Woodhouse118469a2013-01-25 19:46:25 -0600130 dprintf(3, "PrepareToBoot table %04x:%04x\n", regs->es, regs->bx);
131
132 struct e820entry *p = (void *)csm_compat_table.E820Pointer;
133 int i;
134 for (i=0; i < csm_compat_table.E820Length / sizeof(struct e820entry); i++)
135 add_e820(p[i].start, p[i].size, p[i].type);
136
137 if (csm_init_table->HiPmmMemorySizeInBytes > CONFIG_MAX_HIGHTABLE) {
138 u32 hi_pmm_end = csm_init_table->HiPmmMemory + csm_init_table->HiPmmMemorySizeInBytes;
139 add_e820(hi_pmm_end - CONFIG_MAX_HIGHTABLE, CONFIG_MAX_HIGHTABLE, E820_RESERVED);
140 }
141
142 // For PCIBIOS 1ab10e
143 if (csm_compat_table.IrqRoutingTablePointer &&
144 csm_compat_table.IrqRoutingTableLength) {
145 PirAddr = (void *)csm_compat_table.IrqRoutingTablePointer;
146 dprintf(3, "CSM PIRQ table at %p\n", PirAddr);
147 }
148
149 // For find_resume_vector()... and find_pmtimer()
150 if (csm_rsdp.signature == RSDP_SIGNATURE) {
151 RsdpAddr = &csm_rsdp;
152 dprintf(3, "CSM ACPI RSDP at %p\n", RsdpAddr);
David Woodhouse31fe26e2013-02-10 01:15:01 +0000153
154 find_pmtimer();
David Woodhouse118469a2013-01-25 19:46:25 -0600155 }
156
157 // SMBIOS table needs to be copied into the f-seg
158 // XX: OVMF doesn't seem to set SmbiosTableLength so don't check it
159 if (csm_boot_table->SmbiosTable && !SMBiosAddr)
160 copy_smbios((void *)csm_boot_table->SmbiosTable);
161
162 // MPTABLE is just there; we don't care where.
163
164 // EFI may have reinitialised the video using its *own* driver.
165 enable_vga_console();
166
167 // EFI fills this in for us. Zero it for now...
168 struct bios_data_area_s *bda = MAKE_FLATPTR(SEG_BDA, 0);
169 bda->hdcount = 0;
170
171 timer_setup();
David Woodhouse118469a2013-01-25 19:46:25 -0600172 device_hardware_setup();
173 wait_threads();
174 interactive_bootmenu();
175
176 prepareboot();
177
178 regs->ax = 0;
179}
180
181/* Boot */
182static void
183handle_csm_0003(struct bregs *regs)
184{
David Woodhouse84b7dcb2013-02-14 09:17:17 +0000185 if (!CONFIG_BOOT) {
186 regs->ax = 1;
187 return;
188 }
189
David Woodhouse118469a2013-01-25 19:46:25 -0600190 dprintf(3, "Boot\n");
191
192 startBoot();
193
194 regs->ax = 1;
195}
196
197/* Legacy16DispatchOprom */
198static void
199handle_csm_0005(struct bregs *regs)
200{
201 EFI_DISPATCH_OPROM_TABLE *table = MAKE_FLATPTR(regs->es, regs->bx);
202 struct rom_header *rom;
203 u16 bdf;
204
David Woodhouse0feab532013-02-14 09:07:35 +0000205 if (!CONFIG_OPTIONROMS) {
206 regs->ax = 1;
207 return;
208 }
209
David Woodhouse118469a2013-01-25 19:46:25 -0600210 dprintf(3, "Legacy16DispatchOprom rom %p\n", table);
211
212 dprintf(3, "OpromSegment %04x\n", table->OpromSegment);
213 dprintf(3, "RuntimeSegment %04x\n", table->RuntimeSegment);
214 dprintf(3, "PnPInstallationCheck %04x:%04x\n",
215 table->PnPInstallationCheckSegment,
216 table->PnPInstallationCheckOffset);
217 dprintf(3, "RuntimeSegment %04x\n", table->RuntimeSegment);
218
219 rom = MAKE_FLATPTR(table->OpromSegment, 0);
220 bdf = pci_bus_devfn_to_bdf(table->PciBus, table->PciDeviceFunction);
221
David Woodhouse38c2ebf2013-02-09 23:45:02 +0000222 rom_reserve(rom->size * 512);
David Woodhouse118469a2013-01-25 19:46:25 -0600223
224 // XX PnP seg/ofs should never be other than default
225 callrom(rom, bdf);
226
David Woodhouse38c2ebf2013-02-09 23:45:02 +0000227 rom_confirm(rom->size * 512);
228
David Woodhouse118469a2013-01-25 19:46:25 -0600229 regs->bx = 0; // FIXME
230 regs->ax = 0;
231}
232
233/* Legacy16GetTableAddress */
234static void
235handle_csm_0006(struct bregs *regs)
236{
237 u16 size = regs->cx;
238 u16 align = regs->dx;
239 u16 region = regs->bx; // (1 for F000 seg, 2 for E000 seg, 0 for either)
240 void *chunk = NULL;
241
242 if (!region)
243 region = 3;
244
245 dprintf(3, "Legacy16GetTableAddress size %x align %x region %d\n",
246 size, align, region);
247
248 if (region & 2)
249 chunk = pmm_malloc(&ZoneLow, PMM_DEFAULT_HANDLE, size, align);
250 if (!chunk && (region & 1))
251 chunk = pmm_malloc(&ZoneFSeg, PMM_DEFAULT_HANDLE, size, align);
252
253 dprintf(3, "Legacy16GetTableAddress size %x align %x region %d yields %p\n",
254 size, align, region, chunk);
255 if (chunk) {
256 regs->ds = FLATPTR_TO_SEG(chunk);
257 regs->bx = FLATPTR_TO_OFFSET(chunk);
258 regs->ax = 0;
259 } else {
260 regs->ax = 1;
261 }
262}
263
264void VISIBLE32INIT
265handle_csm(struct bregs *regs)
266{
267 ASSERT32FLAT();
268
269 if (!CONFIG_CSM)
270 return;
271
David Woodhouse4b1d2be2013-02-10 00:51:56 +0000272 dprintf(3, "handle_csm regs %p AX=%04x\n", regs, regs->ax);
David Woodhouse118469a2013-01-25 19:46:25 -0600273
274 pic_restore_mask();
275
276 switch(regs->ax) {
277 case 0000: handle_csm_0000(regs); break;
278 case 0001: handle_csm_0001(regs); break;
279 case 0002: handle_csm_0002(regs); break;
280 case 0003: handle_csm_0003(regs); break;
281// case 0004: handle_csm_0004(regs); break;
282 case 0005: handle_csm_0005(regs); break;
283 case 0006: handle_csm_0006(regs); break;
284// case 0007: handle_csm_0007(regs); break;
285// case 0008: hamdle_csm_0008(regs); break;
286 default: regs->al = 1;
287 }
288
David Woodhouse4b1d2be2013-02-10 00:51:56 +0000289 csm_return(regs);
David Woodhouse118469a2013-01-25 19:46:25 -0600290}
291
292int csm_bootprio_ata(struct pci_device *pci, int chanid, int slave)
293{
294 if (!csm_boot_table)
295 return -1;
296 BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable;
297 int index = 1 + (chanid * 2) + slave;
298 dprintf(3, "CSM bootprio for ATA%d,%d (index %d) is %d\n", chanid, slave,
299 index, bbs[index].BootPriority);
300 return bbs[index].BootPriority;
301}
302
303int csm_bootprio_fdc(struct pci_device *pci, int port, int fdid)
304{
305 if (!csm_boot_table)
306 return -1;
307 BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable;
308 dprintf(3, "CSM bootprio for FDC is %d\n", bbs[0].BootPriority);
309 return bbs[0].BootPriority;
310}
311
312int csm_bootprio_pci(struct pci_device *pci)
313{
314 if (!csm_boot_table)
315 return -1;
316 BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable;
317 int i;
318
319 for (i = 5; i < csm_boot_table->NumberBbsEntries; i++) {
320 if (pci->bdf == pci_to_bdf(bbs[i].Bus, bbs[i].Device, bbs[i].Function)) {
321 dprintf(3, "CSM bootprio for PCI(%d,%d,%d) is %d\n", bbs[i].Bus,
322 bbs[i].Device, bbs[i].Function, bbs[i].BootPriority);
323 return bbs[i].BootPriority;
324 }
325 }
326 return -1;
327}