1 // 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 "bregs.h" // struct bregs
8 #include "config.h" // CONFIG_*
9 #include "e820map.h" // e820_add
10 #include "farptr.h" // MAKE_FLATPTR
11 #include "hw/pci.h" // pci_to_bdf
12 #include "hw/pcidevice.h" // pci_probe_devices
13 #include "hw/pic.h" // pic_irqmask_read
14 #include "malloc.h" // malloc_csm_preinit
15 #include "memmap.h" // SYMBOL
16 #include "output.h" // dprintf
17 #include "paravirt.h" // qemu_preinit
18 #include "stacks.h" // wait_threads
19 #include "std/acpi.h" // RSDP_SIGNATURE
20 #include "std/bda.h" // struct bios_data_area_s
21 #include "std/optionrom.h" // struct rom_header
22 #include "util.h" // copy_smbios
23 
24 #define UINT8 u8
25 #define UINT16 u16
26 #define UINT32 u32
27 #include "std/LegacyBios.h"
28 
29 struct rsdp_descriptor csm_rsdp VARFSEG __aligned(16);
30 
31 EFI_COMPATIBILITY16_TABLE csm_compat_table VARFSEG __aligned(16) = {
32     .Signature = 0x24454649,
33     .TableChecksum = 0 /* Filled in by checkrom.py */,
34     .TableLength = sizeof(csm_compat_table),
35     .Compatibility16CallSegment = SEG_BIOS,
36     .Compatibility16CallOffset = 0 /* Filled in by checkrom.py */,
37     .OemIdStringPointer = (u32)"SeaBIOS",
38     .AcpiRsdPtrPointer = (u32)&csm_rsdp,
39 };
40 
41 EFI_TO_COMPATIBILITY16_INIT_TABLE *csm_init_table;
42 EFI_TO_COMPATIBILITY16_BOOT_TABLE *csm_boot_table;
43 
44 static u16 PICMask = PIC_IRQMASK_DEFAULT;
45 
46 extern void __csm_return(struct bregs *regs) __noreturn;
47 
48 static void
csm_return(struct bregs * regs)49 csm_return(struct bregs *regs)
50 {
51     u32 rommax = rom_get_max();
52 
53     dprintf(3, "handle_csm returning AX=%04x\n", regs->ax);
54 
55     csm_compat_table.UmaAddress = rommax;
56     csm_compat_table.UmaSize = SYMBOL(final_readonly_start) - rommax;
57 
58     PICMask = pic_irqmask_read();
59     __csm_return(regs);
60 }
61 
62 static void
csm_maininit(struct bregs * regs)63 csm_maininit(struct bregs *regs)
64 {
65     interface_init();
66     pci_probe_devices();
67 
68     csm_compat_table.PnPInstallationCheckSegment = SEG_BIOS;
69     csm_compat_table.PnPInstallationCheckOffset = get_pnp_offset();
70 
71     regs->ax = 0;
72 
73     csm_return(regs);
74 }
75 
76 /* Legacy16InitializeYourself */
77 static void
handle_csm_0000(struct bregs * regs)78 handle_csm_0000(struct bregs *regs)
79 {
80     qemu_preinit();
81 
82     dprintf(3, "Legacy16InitializeYourself table %04x:%04x\n", regs->es,
83             regs->bx);
84 
85     csm_init_table = MAKE_FLATPTR(regs->es, regs->bx);
86 
87     dprintf(3, "BiosLessThan1MB %08x\n", csm_init_table->BiosLessThan1MB);
88     dprintf(3, "HiPmmMemory     %08x\n", csm_init_table->HiPmmMemory);
89     dprintf(3, "HiPmmMemorySize %08x\n", csm_init_table->HiPmmMemorySizeInBytes);
90     dprintf(3, "ReverseThunk    %04x:%04x\n", csm_init_table->ReverseThunkCallSegment,
91             csm_init_table->ReverseThunkCallOffset);
92     dprintf(3, "NumE820Entries  %08x\n", csm_init_table->NumberE820Entries);
93     dprintf(3, "OsMemoryAbove1M %08x\n", csm_init_table->OsMemoryAbove1Mb);
94     dprintf(3, "ThunkStart      %08x\n", csm_init_table->ThunkStart);
95     dprintf(3, "ThunkSize       %08x\n", csm_init_table->ThunkSizeInBytes);
96     dprintf(3, "LoPmmMemory     %08x\n", csm_init_table->LowPmmMemory);
97     dprintf(3, "LoPmmMemorySize %08x\n", csm_init_table->LowPmmMemorySizeInBytes);
98 
99     malloc_csm_preinit(csm_init_table->LowPmmMemory,
100                        csm_init_table->LowPmmMemorySizeInBytes,
101                        csm_init_table->HiPmmMemory,
102                        csm_init_table->HiPmmMemorySizeInBytes);
103     reloc_preinit(csm_maininit, regs);
104 }
105 
106 /* Legacy16UpdateBbs */
107 static void
handle_csm_0001(struct bregs * regs)108 handle_csm_0001(struct bregs *regs)
109 {
110     if (!CONFIG_BOOT) {
111         regs->ax = 1;
112         return;
113     }
114 
115     dprintf(3, "Legacy16UpdateBbs table %04x:%04x\n", regs->es, regs->bx);
116 
117     csm_boot_table = MAKE_FLATPTR(regs->es, regs->bx);
118     dprintf(3, "MajorVersion %04x\n", csm_boot_table->MajorVersion);
119     dprintf(3, "MinorVersion %04x\n", csm_boot_table->MinorVersion);
120     dprintf(3, "AcpiTable %08x\n", csm_boot_table->AcpiTable);
121     dprintf(3, "SmbiosTable %08x\n", csm_boot_table->SmbiosTable);
122     dprintf(3, "SmbiosTableLength %08x\n", csm_boot_table->SmbiosTableLength);
123 //    dprintf(3, "SioData %08x\n", csm_boot_table->SioData);
124     dprintf(3, "DevicePathType %04x\n", csm_boot_table->DevicePathType);
125     dprintf(3, "PciIrqMask %04x\n", csm_boot_table->PciIrqMask);
126     dprintf(3, "NumberE820Entries %08x\n", csm_boot_table->NumberE820Entries);
127 //    dprintf(3, "HddInfo %08x\n", csm_boot_table->HddInfo);
128     dprintf(3, "NumberBbsEntries %08x\n", csm_boot_table->NumberBbsEntries);
129     dprintf(3, "BBsTable %08x\n", csm_boot_table->BbsTable);
130     dprintf(3, "SmmTable %08x\n", csm_boot_table->SmmTable);
131     dprintf(3, "OsMemoryAbove1Mb %08x\n", csm_boot_table->OsMemoryAbove1Mb);
132     dprintf(3, "UnconventionalDeviceTable %08x\n", csm_boot_table->UnconventionalDeviceTable);
133 
134     regs->ax = 0;
135 }
136 
137 /* PrepareToBoot */
138 static void
handle_csm_0002(struct bregs * regs)139 handle_csm_0002(struct bregs *regs)
140 {
141     if (!CONFIG_BOOT) {
142         regs->ax = 1;
143         return;
144     }
145 
146     dprintf(3, "PrepareToBoot table %04x:%04x\n", regs->es, regs->bx);
147 
148     struct e820entry *p = (void *)csm_compat_table.E820Pointer;
149     int i;
150     for (i=0; i < csm_compat_table.E820Length / sizeof(struct e820entry); i++)
151         e820_add(p[i].start, p[i].size, p[i].type);
152 
153     if (csm_init_table->HiPmmMemorySizeInBytes > BUILD_MAX_HIGHTABLE) {
154         u32 hi_pmm_end = csm_init_table->HiPmmMemory + csm_init_table->HiPmmMemorySizeInBytes;
155         e820_add(hi_pmm_end - BUILD_MAX_HIGHTABLE, BUILD_MAX_HIGHTABLE, E820_RESERVED);
156     }
157 
158     // For PCIBIOS 1ab10e
159     if (csm_compat_table.IrqRoutingTablePointer &&
160         csm_compat_table.IrqRoutingTableLength) {
161         PirAddr = (void *)csm_compat_table.IrqRoutingTablePointer;
162         dprintf(3, "CSM PIRQ table at %p\n", PirAddr);
163     }
164 
165     // For find_resume_vector()... and find_acpi_features()
166     if (csm_rsdp.signature == RSDP_SIGNATURE) {
167         RsdpAddr = &csm_rsdp;
168         dprintf(3, "CSM ACPI RSDP at %p\n", RsdpAddr);
169 
170         find_acpi_features();
171     }
172 
173     // SMBIOS table needs to be copied into the f-seg
174     // XX: OVMF doesn't seem to set SmbiosTableLength so don't check it
175     if (csm_boot_table->SmbiosTable && !SMBiosAddr)
176         copy_smbios((void *)csm_boot_table->SmbiosTable);
177 
178     // MPTABLE is just there; we don't care where.
179 
180     // EFI may have reinitialised the video using its *own* driver.
181     enable_vga_console();
182 
183     // EFI fills this in for us. Zero it for now...
184     struct bios_data_area_s *bda = MAKE_FLATPTR(SEG_BDA, 0);
185     bda->hdcount = 0;
186 
187     thread_setup();
188     mathcp_setup();
189     timer_setup();
190     clock_setup();
191     device_hardware_setup();
192     wait_threads();
193     interactive_bootmenu();
194 
195     prepareboot();
196 
197     regs->ax = 0;
198 }
199 
200 /* Boot */
201 static void
handle_csm_0003(struct bregs * regs)202 handle_csm_0003(struct bregs *regs)
203 {
204     if (!CONFIG_BOOT) {
205         regs->ax = 1;
206         return;
207     }
208 
209     dprintf(3, "Boot\n");
210 
211     startBoot();
212 
213     regs->ax = 1;
214 }
215 
216 /* Legacy16DispatchOprom */
217 static void
handle_csm_0005(struct bregs * regs)218 handle_csm_0005(struct bregs *regs)
219 {
220     EFI_DISPATCH_OPROM_TABLE *table = MAKE_FLATPTR(regs->es, regs->bx);
221     struct rom_header *rom;
222     u16 bdf;
223 
224     if (!CONFIG_OPTIONROMS) {
225         regs->ax = 1;
226         return;
227     }
228 
229     dprintf(3, "Legacy16DispatchOprom rom %p\n", table);
230 
231     dprintf(3, "OpromSegment   %04x\n", table->OpromSegment);
232     dprintf(3, "RuntimeSegment %04x\n", table->RuntimeSegment);
233     dprintf(3, "PnPInstallationCheck %04x:%04x\n",
234             table->PnPInstallationCheckSegment,
235             table->PnPInstallationCheckOffset);
236     dprintf(3, "RuntimeSegment %04x\n", table->RuntimeSegment);
237 
238     rom = MAKE_FLATPTR(table->OpromSegment, 0);
239     bdf = pci_bus_devfn_to_bdf(table->PciBus, table->PciDeviceFunction);
240 
241     rom_reserve(rom->size * 512);
242 
243     // XX PnP seg/ofs should never be other than default
244     callrom(rom, bdf);
245 
246     rom_confirm(rom->size * 512);
247 
248     regs->bx = 0; // FIXME
249     regs->ax = 0;
250 }
251 
252 /* Legacy16GetTableAddress */
253 static void
handle_csm_0006(struct bregs * regs)254 handle_csm_0006(struct bregs *regs)
255 {
256     u16 size = regs->cx;
257     u16 align = regs->dx;
258     u16 region = regs->bx; // (1 for F000 seg, 2 for E000 seg, 0 for either)
259     void *chunk = NULL;
260 
261     dprintf(3, "Legacy16GetTableAddress size %x align %x region %d\n",
262         size, align, region);
263 
264     if (!region)
265         region = 3;
266 
267     // DX = Required address alignment. Bit mapped.
268     // First non-zero bit from the right is the alignment.*/
269     if (align) {
270         align = 1 << __ffs(align);
271         if (align < MALLOC_MIN_ALIGN)
272             align = MALLOC_MIN_ALIGN;
273     } else {
274         align = MALLOC_MIN_ALIGN;
275     }
276 
277     if (region & 2)
278         chunk = _malloc(&ZoneLow, size, align);
279     if (!chunk && (region & 1))
280         chunk = _malloc(&ZoneFSeg, size, align);
281 
282     dprintf(3, "Legacy16GetTableAddress size %x align %x region %d yields %p\n",
283         size, align, region, chunk);
284     if (chunk) {
285         regs->ds = FLATPTR_TO_SEG(chunk);
286         regs->bx = FLATPTR_TO_OFFSET(chunk);
287         regs->ax = 0;
288     } else {
289         regs->ax = 1;
290     }
291 }
292 
293 void VISIBLE32INIT
handle_csm(struct bregs * regs)294 handle_csm(struct bregs *regs)
295 {
296     ASSERT32FLAT();
297 
298     if (!CONFIG_CSM)
299         return;
300 
301     dprintf(3, "handle_csm regs %p AX=%04x\n", regs, regs->ax);
302 
303     code_mutable_preinit();
304     pic_irqmask_write(PICMask);
305 
306     switch(regs->ax) {
307     case 0000: handle_csm_0000(regs); break;
308     case 0001: handle_csm_0001(regs); break;
309     case 0002: handle_csm_0002(regs); break;
310     case 0003: handle_csm_0003(regs); break;
311 //    case 0004: handle_csm_0004(regs); break;
312     case 0005: handle_csm_0005(regs); break;
313     case 0006: handle_csm_0006(regs); break;
314 //    case 0007: handle_csm_0007(regs); break;
315 //    case 0008: hamdle_csm_0008(regs); break;
316     default: regs->al = 1;
317     }
318 
319     csm_return(regs);
320 }
321 
csm_prio_to_seabios(u16 csm_prio)322 static int csm_prio_to_seabios(u16 csm_prio)
323 {
324     switch (csm_prio) {
325     case BBS_DO_NOT_BOOT_FROM:
326     case BBS_IGNORE_ENTRY:
327         return -1;
328 
329     case BBS_LOWEST_PRIORITY:
330     case BBS_UNPRIORITIZED_ENTRY:
331     default:
332         // SeaBIOS default priorities start at 1, with 0 being used for
333         // an item explicitly selected from interactive_bootmenu().
334         // As in find_prio(), add 1 to the value being returned.
335         return csm_prio + 1;
336     }
337 }
338 
csm_bootprio_ata(struct pci_device * pci,int chanid,int slave)339 int csm_bootprio_ata(struct pci_device *pci, int chanid, int slave)
340 {
341     if (!csm_boot_table)
342         return -1;
343     BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable;
344     int index = 1 + (chanid * 2) + slave;
345     dprintf(3, "CSM bootprio for ATA%d,%d (index %d) is %d\n", chanid, slave,
346             index, bbs[index].BootPriority);
347     return csm_prio_to_seabios(bbs[index].BootPriority);
348 }
349 
csm_bootprio_fdc(struct pci_device * pci,int port,int fdid)350 int csm_bootprio_fdc(struct pci_device *pci, int port, int fdid)
351 {
352     if (!csm_boot_table)
353         return -1;
354     BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable;
355     dprintf(3, "CSM bootprio for FDC is %d\n", bbs[0].BootPriority);
356     return csm_prio_to_seabios(bbs[0].BootPriority);
357 }
358 
csm_bootprio_pci(struct pci_device * pci)359 int csm_bootprio_pci(struct pci_device *pci)
360 {
361     if (!csm_boot_table)
362         return -1;
363     BBS_TABLE *bbs = (void *)csm_boot_table->BbsTable;
364     int i;
365 
366     for (i = 5; i < csm_boot_table->NumberBbsEntries; i++) {
367         if (pci->bdf == pci_to_bdf(bbs[i].Bus, bbs[i].Device, bbs[i].Function)) {
368             dprintf(3, "CSM bootprio for PCI(%d,%d,%d) is %d\n", bbs[i].Bus,
369                     bbs[i].Device, bbs[i].Function, bbs[i].BootPriority);
370             return csm_prio_to_seabios(bbs[i].BootPriority);
371         }
372     }
373     return -1;
374 }
375