blob: 7cadd12e5b99cda517fce05a4b9625249db0e129 [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
Kevin O'Connorc167e542015-09-29 09:40:46 -04007#include "bregs.h" // struct bregs
David Woodhouse118469a2013-01-25 19:46:25 -06008#include "config.h" // CONFIG_*
Kevin O'Connorc167e542015-09-29 09:40:46 -04009#include "e820map.h" // e820_add
Kevin O'Connor392d2aa2013-09-15 00:14:28 -040010#include "farptr.h" // MAKE_FLATPTR
Kevin O'Connorc167e542015-09-29 09:40:46 -040011#include "hw/pci.h" // pci_probe_devices
12#include "hw/pic.h" // pic_irqmask_read
Kevin O'Connor7d0393d2015-10-15 13:53:04 -040013#include "malloc.h" // malloc_csm_preinit
Kevin O'Connor5d912262015-10-08 11:18:17 -040014#include "memmap.h" // SYMBOL
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040015#include "output.h" // dprintf
Kevin O'Connorc167e542015-09-29 09:40:46 -040016#include "paravirt.h" // qemu_preinit
Kevin O'Connor3df600b2013-09-14 19:28:55 -040017#include "stacks.h" // wait_threads
Kevin O'Connor5a7545c2013-09-14 22:54:44 -040018#include "std/acpi.h" // RSDP_SIGNATURE
Kevin O'Connor392d2aa2013-09-15 00:14:28 -040019#include "std/bda.h" // struct bios_data_area_s
Kevin O'Connor8fb3a5e2013-09-14 22:27:14 -040020#include "std/optionrom.h" // struct rom_header
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040021#include "util.h" // copy_smbios
David Woodhouse118469a2013-01-25 19:46:25 -060022
Kevin O'Connorf6d700d2013-09-14 22:38:00 -040023#define UINT8 u8
24#define UINT16 u16
25#define UINT32 u32
26#include "std/LegacyBios.h"
27
Kevin O'Connor89a2f962013-02-18 23:36:03 -050028struct rsdp_descriptor csm_rsdp VARFSEG __aligned(16);
David Woodhouse118469a2013-01-25 19:46:25 -060029
Kevin O'Connor89a2f962013-02-18 23:36:03 -050030EFI_COMPATIBILITY16_TABLE csm_compat_table VARFSEG __aligned(16) = {
David Woodhouse118469a2013-01-25 19:46:25 -060031 .Signature = 0x24454649,
32 .TableChecksum = 0 /* Filled in by checkrom.py */,
33 .TableLength = sizeof(csm_compat_table),
34 .Compatibility16CallSegment = SEG_BIOS,
35 .Compatibility16CallOffset = 0 /* Filled in by checkrom.py */,
36 .OemIdStringPointer = (u32)"SeaBIOS",
37 .AcpiRsdPtrPointer = (u32)&csm_rsdp,
38};
39
David Woodhouse118469a2013-01-25 19:46:25 -060040EFI_TO_COMPATIBILITY16_INIT_TABLE *csm_init_table;
41EFI_TO_COMPATIBILITY16_BOOT_TABLE *csm_boot_table;
42
Kevin O'Connoraa7c2342013-07-14 15:07:21 -040043static u16 PICMask = PIC_IRQMASK_DEFAULT;
44
David Woodhouse4b1d2be2013-02-10 00:51:56 +000045extern void __csm_return(struct bregs *regs) __noreturn;
46
47static void
48csm_return(struct bregs *regs)
49{
David Woodhousef9645c72014-06-02 14:00:14 +010050 u32 rommax = rom_get_max();
David Woodhousef9645c72014-06-02 14:00:14 +010051
David Woodhouse4b1d2be2013-02-10 00:51:56 +000052 dprintf(3, "handle_csm returning AX=%04x\n", regs->ax);
53
David Woodhousef9645c72014-06-02 14:00:14 +010054 csm_compat_table.UmaAddress = rommax;
Kevin O'Connor5d912262015-10-08 11:18:17 -040055 csm_compat_table.UmaSize = SYMBOL(final_readonly_start) - rommax;
David Woodhousef9645c72014-06-02 14:00:14 +010056
Kevin O'Connoraa7c2342013-07-14 15:07:21 -040057 PICMask = pic_irqmask_read();
David Woodhouse4b1d2be2013-02-10 00:51:56 +000058 __csm_return(regs);
59}
David Woodhouse118469a2013-01-25 19:46:25 -060060
61static void
62csm_maininit(struct bregs *regs)
63{
64 interface_init();
65 pci_probe_devices();
66
67 csm_compat_table.PnPInstallationCheckSegment = SEG_BIOS;
68 csm_compat_table.PnPInstallationCheckOffset = get_pnp_offset();
69
70 regs->ax = 0;
71
David Woodhouse118469a2013-01-25 19:46:25 -060072 csm_return(regs);
73}
74
75/* Legacy16InitializeYourself */
76static void
77handle_csm_0000(struct bregs *regs)
78{
Gerd Hoffmann9ab39de2013-09-03 11:52:13 +020079 qemu_preinit();
80
David Woodhouse118469a2013-01-25 19:46:25 -060081 dprintf(3, "Legacy16InitializeYourself table %04x:%04x\n", regs->es,
82 regs->bx);
83
84 csm_init_table = MAKE_FLATPTR(regs->es, regs->bx);
85
86 dprintf(3, "BiosLessThan1MB %08x\n", csm_init_table->BiosLessThan1MB);
87 dprintf(3, "HiPmmMemory %08x\n", csm_init_table->HiPmmMemory);
88 dprintf(3, "HiPmmMemorySize %08x\n", csm_init_table->HiPmmMemorySizeInBytes);
89 dprintf(3, "ReverseThunk %04x:%04x\n", csm_init_table->ReverseThunkCallSegment,
90 csm_init_table->ReverseThunkCallOffset);
91 dprintf(3, "NumE820Entries %08x\n", csm_init_table->NumberE820Entries);
92 dprintf(3, "OsMemoryAbove1M %08x\n", csm_init_table->OsMemoryAbove1Mb);
93 dprintf(3, "ThunkStart %08x\n", csm_init_table->ThunkStart);
94 dprintf(3, "ThunkSize %08x\n", csm_init_table->ThunkSizeInBytes);
95 dprintf(3, "LoPmmMemory %08x\n", csm_init_table->LowPmmMemory);
96 dprintf(3, "LoPmmMemorySize %08x\n", csm_init_table->LowPmmMemorySizeInBytes);
97
Kevin O'Connor7d0393d2015-10-15 13:53:04 -040098 malloc_csm_preinit(csm_init_table->LowPmmMemory,
David Woodhouse118469a2013-01-25 19:46:25 -060099 csm_init_table->LowPmmMemorySizeInBytes,
100 csm_init_table->HiPmmMemory,
101 csm_init_table->HiPmmMemorySizeInBytes);
102 reloc_preinit(csm_maininit, regs);
103}
104
105/* Legacy16UpdateBbs */
106static void
107handle_csm_0001(struct bregs *regs)
108{
David Woodhouse84b7dcb2013-02-14 09:17:17 +0000109 if (!CONFIG_BOOT) {
110 regs->ax = 1;
111 return;
112 }
113
David Woodhouse118469a2013-01-25 19:46:25 -0600114 dprintf(3, "Legacy16UpdateBbs table %04x:%04x\n", regs->es, regs->bx);
115
116 csm_boot_table = MAKE_FLATPTR(regs->es, regs->bx);
117 dprintf(3, "MajorVersion %04x\n", csm_boot_table->MajorVersion);
118 dprintf(3, "MinorVersion %04x\n", csm_boot_table->MinorVersion);
119 dprintf(3, "AcpiTable %08x\n", csm_boot_table->AcpiTable);
120 dprintf(3, "SmbiosTable %08x\n", csm_boot_table->SmbiosTable);
121 dprintf(3, "SmbiosTableLength %08x\n", csm_boot_table->SmbiosTableLength);
122// dprintf(3, "SioData %08x\n", csm_boot_table->SioData);
123 dprintf(3, "DevicePathType %04x\n", csm_boot_table->DevicePathType);
124 dprintf(3, "PciIrqMask %04x\n", csm_boot_table->PciIrqMask);
125 dprintf(3, "NumberE820Entries %08x\n", csm_boot_table->NumberE820Entries);
126// dprintf(3, "HddInfo %08x\n", csm_boot_table->HddInfo);
127 dprintf(3, "NumberBbsEntries %08x\n", csm_boot_table->NumberBbsEntries);
128 dprintf(3, "BBsTable %08x\n", csm_boot_table->BbsTable);
129 dprintf(3, "SmmTable %08x\n", csm_boot_table->SmmTable);
130 dprintf(3, "OsMemoryAbove1Mb %08x\n", csm_boot_table->OsMemoryAbove1Mb);
131 dprintf(3, "UnconventionalDeviceTable %08x\n", csm_boot_table->UnconventionalDeviceTable);
132
133 regs->ax = 0;
134}
135
136/* PrepareToBoot */
137static void
138handle_csm_0002(struct bregs *regs)
139{
David Woodhouse84b7dcb2013-02-14 09:17:17 +0000140 if (!CONFIG_BOOT) {
141 regs->ax = 1;
142 return;
143 }
144
David Woodhouse118469a2013-01-25 19:46:25 -0600145 dprintf(3, "PrepareToBoot table %04x:%04x\n", regs->es, regs->bx);
146
147 struct e820entry *p = (void *)csm_compat_table.E820Pointer;
148 int i;
149 for (i=0; i < csm_compat_table.E820Length / sizeof(struct e820entry); i++)
Kevin O'Connorc167e542015-09-29 09:40:46 -0400150 e820_add(p[i].start, p[i].size, p[i].type);
David Woodhouse118469a2013-01-25 19:46:25 -0600151
Kevin O'Connore52ad392013-02-20 23:48:22 -0500152 if (csm_init_table->HiPmmMemorySizeInBytes > BUILD_MAX_HIGHTABLE) {
David Woodhouse118469a2013-01-25 19:46:25 -0600153 u32 hi_pmm_end = csm_init_table->HiPmmMemory + csm_init_table->HiPmmMemorySizeInBytes;
Kevin O'Connorc167e542015-09-29 09:40:46 -0400154 e820_add(hi_pmm_end - BUILD_MAX_HIGHTABLE, BUILD_MAX_HIGHTABLE, E820_RESERVED);
David Woodhouse118469a2013-01-25 19:46:25 -0600155 }
156
157 // For PCIBIOS 1ab10e
158 if (csm_compat_table.IrqRoutingTablePointer &&
159 csm_compat_table.IrqRoutingTableLength) {
160 PirAddr = (void *)csm_compat_table.IrqRoutingTablePointer;
161 dprintf(3, "CSM PIRQ table at %p\n", PirAddr);
162 }
163
David Woodhoused304fe42013-02-23 00:24:48 +0000164 // For find_resume_vector()... and find_acpi_features()
David Woodhouse118469a2013-01-25 19:46:25 -0600165 if (csm_rsdp.signature == RSDP_SIGNATURE) {
166 RsdpAddr = &csm_rsdp;
167 dprintf(3, "CSM ACPI RSDP at %p\n", RsdpAddr);
David Woodhouse31fe26e2013-02-10 01:15:01 +0000168
David Woodhoused304fe42013-02-23 00:24:48 +0000169 find_acpi_features();
David Woodhouse118469a2013-01-25 19:46:25 -0600170 }
171
172 // SMBIOS table needs to be copied into the f-seg
173 // XX: OVMF doesn't seem to set SmbiosTableLength so don't check it
174 if (csm_boot_table->SmbiosTable && !SMBiosAddr)
175 copy_smbios((void *)csm_boot_table->SmbiosTable);
176
177 // MPTABLE is just there; we don't care where.
178
179 // EFI may have reinitialised the video using its *own* driver.
180 enable_vga_console();
181
182 // EFI fills this in for us. Zero it for now...
183 struct bios_data_area_s *bda = MAKE_FLATPTR(SEG_BDA, 0);
184 bda->hdcount = 0;
185
Kevin O'Connor8b9942f2015-07-14 15:44:26 -0400186 thread_setup();
Kevin O'Connor7093aa52013-07-21 20:01:07 -0400187 mathcp_setup();
David Woodhouse118469a2013-01-25 19:46:25 -0600188 timer_setup();
Kevin O'Connorc6e8c072013-07-20 10:51:58 -0400189 clock_setup();
David Woodhouse118469a2013-01-25 19:46:25 -0600190 device_hardware_setup();
191 wait_threads();
192 interactive_bootmenu();
193
194 prepareboot();
195
196 regs->ax = 0;
197}
198
199/* Boot */
200static void
201handle_csm_0003(struct bregs *regs)
202{
David Woodhouse84b7dcb2013-02-14 09:17:17 +0000203 if (!CONFIG_BOOT) {
204 regs->ax = 1;
205 return;
206 }
207
David Woodhouse118469a2013-01-25 19:46:25 -0600208 dprintf(3, "Boot\n");
209
210 startBoot();
211
212 regs->ax = 1;
213}
214
215/* Legacy16DispatchOprom */
216static void
217handle_csm_0005(struct bregs *regs)
218{
219 EFI_DISPATCH_OPROM_TABLE *table = MAKE_FLATPTR(regs->es, regs->bx);
220 struct rom_header *rom;
221 u16 bdf;
222
David Woodhouse0feab532013-02-14 09:07:35 +0000223 if (!CONFIG_OPTIONROMS) {
224 regs->ax = 1;
225 return;
226 }
227
David Woodhouse118469a2013-01-25 19:46:25 -0600228 dprintf(3, "Legacy16DispatchOprom rom %p\n", table);
229
230 dprintf(3, "OpromSegment %04x\n", table->OpromSegment);
231 dprintf(3, "RuntimeSegment %04x\n", table->RuntimeSegment);
232 dprintf(3, "PnPInstallationCheck %04x:%04x\n",
233 table->PnPInstallationCheckSegment,
234 table->PnPInstallationCheckOffset);
235 dprintf(3, "RuntimeSegment %04x\n", table->RuntimeSegment);
236
237 rom = MAKE_FLATPTR(table->OpromSegment, 0);
238 bdf = pci_bus_devfn_to_bdf(table->PciBus, table->PciDeviceFunction);
239
David Woodhouse38c2ebf2013-02-09 23:45:02 +0000240 rom_reserve(rom->size * 512);
David Woodhouse118469a2013-01-25 19:46:25 -0600241
242 // XX PnP seg/ofs should never be other than default
243 callrom(rom, bdf);
244
David Woodhouse38c2ebf2013-02-09 23:45:02 +0000245 rom_confirm(rom->size * 512);
246
David Woodhouse118469a2013-01-25 19:46:25 -0600247 regs->bx = 0; // FIXME
248 regs->ax = 0;
249}
250
251/* Legacy16GetTableAddress */
252static void
253handle_csm_0006(struct bregs *regs)
254{
255 u16 size = regs->cx;
256 u16 align = regs->dx;
257 u16 region = regs->bx; // (1 for F000 seg, 2 for E000 seg, 0 for either)
258 void *chunk = NULL;
259
260 if (!region)
261 region = 3;
262
263 dprintf(3, "Legacy16GetTableAddress size %x align %x region %d\n",
264 size, align, region);
265
266 if (region & 2)
Kevin O'Connor743c1572013-12-14 13:14:02 -0500267 chunk = _malloc(&ZoneLow, size, align);
David Woodhouse118469a2013-01-25 19:46:25 -0600268 if (!chunk && (region & 1))
Kevin O'Connor743c1572013-12-14 13:14:02 -0500269 chunk = _malloc(&ZoneFSeg, size, align);
David Woodhouse118469a2013-01-25 19:46:25 -0600270
271 dprintf(3, "Legacy16GetTableAddress size %x align %x region %d yields %p\n",
272 size, align, region, chunk);
273 if (chunk) {
274 regs->ds = FLATPTR_TO_SEG(chunk);
275 regs->bx = FLATPTR_TO_OFFSET(chunk);
276 regs->ax = 0;
277 } else {
278 regs->ax = 1;
279 }
280}
281
282void VISIBLE32INIT
283handle_csm(struct bregs *regs)
284{
285 ASSERT32FLAT();
286
287 if (!CONFIG_CSM)
288 return;
289
David Woodhouse4b1d2be2013-02-10 00:51:56 +0000290 dprintf(3, "handle_csm regs %p AX=%04x\n", regs, regs->ax);
David Woodhouse118469a2013-01-25 19:46:25 -0600291
Kevin O'Connoraa7c2342013-07-14 15:07:21 -0400292 pic_irqmask_write(PICMask);
David Woodhouse118469a2013-01-25 19:46:25 -0600293
294 switch(regs->ax) {
295 case 0000: handle_csm_0000(regs); break;
296 case 0001: handle_csm_0001(regs); break;
297 case 0002: handle_csm_0002(regs); break;
298 case 0003: handle_csm_0003(regs); break;
299// case 0004: handle_csm_0004(regs); break;
300 case 0005: handle_csm_0005(regs); break;
301 case 0006: handle_csm_0006(regs); break;
302// case 0007: handle_csm_0007(regs); break;
303// case 0008: hamdle_csm_0008(regs); break;
304 default: regs->al = 1;
305 }
306
David Woodhouse4b1d2be2013-02-10 00:51:56 +0000307 csm_return(regs);
David Woodhouse118469a2013-01-25 19:46:25 -0600308}
309
310int csm_bootprio_ata(struct pci_device *pci, int chanid, int slave)
311{
312 if (!csm_boot_table)
313 return -1;
314 BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable;
315 int index = 1 + (chanid * 2) + slave;
316 dprintf(3, "CSM bootprio for ATA%d,%d (index %d) is %d\n", chanid, slave,
317 index, bbs[index].BootPriority);
318 return bbs[index].BootPriority;
319}
320
321int csm_bootprio_fdc(struct pci_device *pci, int port, int fdid)
322{
323 if (!csm_boot_table)
324 return -1;
325 BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable;
326 dprintf(3, "CSM bootprio for FDC is %d\n", bbs[0].BootPriority);
327 return bbs[0].BootPriority;
328}
329
330int csm_bootprio_pci(struct pci_device *pci)
331{
332 if (!csm_boot_table)
333 return -1;
334 BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable;
335 int i;
336
337 for (i = 5; i < csm_boot_table->NumberBbsEntries; i++) {
338 if (pci->bdf == pci_to_bdf(bbs[i].Bus, bbs[i].Device, bbs[i].Function)) {
339 dprintf(3, "CSM bootprio for PCI(%d,%d,%d) is %d\n", bbs[i].Bus,
340 bbs[i].Device, bbs[i].Function, bbs[i].BootPriority);
341 return bbs[i].BootPriority;
342 }
343 }
344 return -1;
345}