diff options
Diffstat (limited to 'roms/qboot')
39 files changed, 3828 insertions, 0 deletions
diff --git a/roms/qboot/LICENSE b/roms/qboot/LICENSE new file mode 100644 index 000000000..8cdb8451d --- /dev/null +++ b/roms/qboot/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/> + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. + diff --git a/roms/qboot/README b/roms/qboot/README new file mode 100644 index 000000000..c91335709 --- /dev/null +++ b/roms/qboot/README @@ -0,0 +1,47 @@ +A simple x86 firmware that can boot Linux. + +Most of QEMU's startup time is spent: + +* in the dynamic linker. This can be reduced by 150 ms simply by + compiling a stripped down QEMU: + + ./configure --disable-libssh2 --disable-tcmalloc --disable-glusterfs \ + --disable-seccomp --disable-{bzip2,snappy,lzo} --disable-usb-redir \ + --disable-libusb --disable-smartcard-nss --disable-libnfs \ + --disable-libiscsi --disable-rbd --disable-spice --disable-attr \ + --disable-cap-ng --disable-linux-aio --disable-brlapi \ + --disable-vnc-{jpeg,tls,sasl,png,ws} --disable-rdma --disable-bluez \ + --disable-fdt --disable-curl --disable-curses --disable-sdl \ + --disable-gtk --disable-tpm --disable-vte --disable-vnc \ + --disable-xen --disable-opengl --target-list=x86_64-softmmu + +* in the BIOS. qboot saves another 150 ms. + +* until QEMU 2.7+, in fw_cfg. qboot uses the DMA interface which is pretty + much instantaneous. + +Compile qboot +============= + +Clone the source: + + $ git clone https://github.com/bonzini/qboot.git + +Compile the qboot firmware (you may need to install the relevant build +time dependancies): + + $ meson build && ninja -C build + +The result will be a 64K file named bios.bin under the build/ directory. + +Usage +===== + + $ qemu-kvm -bios bios.bin \ + -kernel /boot/vmlinuz-4.0.3-300.fc22.x86_64 \ + -serial mon:stdio -append 'console=ttyS0,115200,8n1' + +TODO +==== + +* Add the possibility to configure out PIC and PCI bridge initialization diff --git a/roms/qboot/benchmark.h b/roms/qboot/benchmark.h new file mode 100644 index 000000000..2be08e7b2 --- /dev/null +++ b/roms/qboot/benchmark.h @@ -0,0 +1,14 @@ +#ifndef BENCHMARK_H +#define BENCHMARK_H + +/* IO ports for different exit points */ +#define LINUX_EXIT_PORT 0xf4 +#define FW_EXIT_PORT 0xf5 + +/* Exit point values */ +#define FW_START 1 +#define LINUX_START_FWCFG 2 +#define LINUX_START_BOOT 3 +#define LINUX_START_PVHBOOT 4 + +#endif /* BENCHMARK_H */ diff --git a/roms/qboot/code16.c b/roms/qboot/code16.c new file mode 100644 index 000000000..2fd7ef962 --- /dev/null +++ b/roms/qboot/code16.c @@ -0,0 +1,122 @@ +asm(".code16gcc"); +#include <stddef.h> +#include "bios.h" +#include "segment.h" +#include "ioport.h" +#include "processor-flags.h" +#include "e820.h" + +static inline void set_fs(uint16_t seg) +{ + asm volatile("movw %0,%%fs" : : "rm" (seg)); +} + +static inline uint8_t rdfs8(unsigned long addr) +{ + uint8_t v; + + asm volatile("addr32 movb %%fs:%1,%0" : "=q" (v) : "m" (*(uint8_t *)addr)); + + return v; +} + +static inline uint32_t rdfs32(unsigned long addr) +{ + uint32_t v; + + asm volatile("addr32 movl %%fs:%1,%0" : "=r" (v) : "m" (*(uint32_t *)addr)); + + return v; +} + +static inline uint16_t rdcs16(void *p) +{ + uint32_t addr = ((uintptr_t) p) & 65535; + uint16_t v; + + asm volatile("addr32 movw %%cs:%1,%0" : "=r" (v) : "m" (*(uint32_t *)addr)); + + return v; +} + +uint16_t e820_seg; + +bioscall void e820_query_map(struct biosregs *regs) +{ + uint32_t map_size; + uint32_t ndx; + + set_fs(rdcs16(&e820_seg)); + + ndx = regs->ebx; + + map_size = rdfs32(offsetof(struct e820map, nr_map)); + + if (ndx < map_size) { + uint32_t start; + unsigned int i; + uint8_t *p; + + start = offsetof(struct e820map, map[ndx]); + + p = (void *) regs->edi; + + for (i = 0; i < sizeof(struct e820entry); i++) + *p++ = rdfs8(start + i); + } + + regs->eax = SMAP; + regs->ecx = sizeof(struct e820entry); + regs->ebx = ++ndx; + + if (ndx >= map_size) + regs->ebx = 0; /* end of map */ +} + +bioscall void int15_handler(struct biosregs *regs) +{ + switch (regs->eax) { + case 0xe820: + e820_query_map(regs); + break; + default: + /* Set CF to indicate failure. */ + regs->eflags |= X86_EFLAGS_CF; + break; + } +} +/* + * It's probably much more useful to make this print to the serial + * line rather than print to a non-displayed VGA memory + */ +static inline void int10_putchar(struct biosregs *args) +{ + uint8_t al = args->eax & 0xFF; + + outb(0x3f8, al); +} + +#define VBE_STATUS_OK 0x004F +#define VBE_STATUS_FAIL 0x014F + +static void int10_vesa(struct biosregs *args) +{ + args->eax = VBE_STATUS_FAIL; +} + +bioscall void int10_handler(struct biosregs *args) +{ + uint8_t ah; + + ah = (args->eax & 0xff00) >> 8; + + switch (ah) { + case 0x0e: + int10_putchar(args); + break; + case 0x4f: + int10_vesa(args); + break; + } + +} diff --git a/roms/qboot/code32seg.c b/roms/qboot/code32seg.c new file mode 100644 index 000000000..e829c0332 --- /dev/null +++ b/roms/qboot/code32seg.c @@ -0,0 +1,75 @@ +#include <stddef.h> +#include "bios.h" +#include "pci.h" +#include "processor-flags.h" + +#define PCI_FUNC_NOT_SUPPORTED 0x81 +#define PCI_BAD_VENDOR_ID 0x83 +#define PCI_DEVICE_NOT_FOUND 0x86 +#define PCI_BUFFER_TOO_SMALL 0x89 + +/* + * The PCIBIOS handler must be position independent. To read a flat pointer, + * we use the instruction pointer to retrieve the address corresponding to + * physical address 0 (i.e., what Linux calls PAGE_OFFSET). + */ + +static inline void *from_flat_ptr(void *p) +{ + return p + pic_base(); +} + +#define FLAT_VAR(x) (*(typeof(&(x))) from_flat_ptr(&(x))) + +#pragma GCC optimize("no-jump-tables") + +bioscall void pcibios_handler(struct bios32regs *args) +{ + switch (args->eax) { + /* discovery */ + case 0xb101: + args->eax = 0x01; + args->ebx = 0x210; + args->ecx = FLAT_VAR(max_bus); + args->edx = 0x20494350; + goto success; + + /* config space access */ + case 0xb108: + args->ecx = pci_config_readb(args->ebx, args->edi); + goto success; + case 0xb109: + args->ecx = pci_config_readw(args->ebx, args->edi); + goto success; + case 0xb10a: + args->ecx = pci_config_readl(args->ebx, args->edi); + goto success; + case 0xb10b: + pci_config_writeb(args->ebx, args->edi, args->ecx); + goto success; + case 0xb10c: + pci_config_writew(args->ebx, args->edi, args->ecx); + goto success; + case 0xb10d: + pci_config_writel(args->ebx, args->edi, args->ecx); + goto success; + + /* find device id, find class code */ + case 0xb102: + case 0xb103: + args->eax &= ~0xff00; + args->eax |= PCI_DEVICE_NOT_FOUND << 8; + break; + + default: + args->eax &= ~0xff00; + args->eax |= PCI_FUNC_NOT_SUPPORTED << 8; + break; + } + args->eflags |= X86_EFLAGS_CF; + return; + +success: + /* On entry, CF=0 */ + args->eax &= ~0xff00; /* clear ah */ +} diff --git a/roms/qboot/cstart.S b/roms/qboot/cstart.S new file mode 100644 index 000000000..6f5741cf3 --- /dev/null +++ b/roms/qboot/cstart.S @@ -0,0 +1,45 @@ +.code16gcc +#include "assembly.h" +.section .init +ENTRY(pm_entry) + xor %ax, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + mov $0x7c00, %sp + + mov %cr0, %eax + and $~((1 << 30) | (1 << 29)), %eax # clear CD and NW + or $1, %al + mov %eax, %cr0 + lgdtl %cs:0xff80 + gdt32_descr - pm_entry + ljmpl $8, $0xffffff80 + 2f - pm_entry +2: + .code32 + mov $16, %ax + mov %ax, %ds + mov %ax, %es + mov %ax, %fs + mov %ax, %gs + mov %ax, %ss + ljmp $8, $0xffff0000 + +gdt32: + .quad 0 + .quad 0x00cf9b000000ffff // flat 32-bit code segment + .quad 0x00cf93000000ffff // flat 32-bit data segment + .quad 0x000f9b0f0000ffff // 64K 16-bit code segment at 0xF0000 + .quad 0x000f93000000ffff // 64K 16-bit data segment at 0x0 +gdt32_end: + +gdt32_descr: + .word gdt32_end - gdt32 - 1 + .long 0xffffff80 + gdt32 - pm_entry +ENTRY_END(pm_entry) + + .code16gcc + .section .resetvector + jmp pm_entry + diff --git a/roms/qboot/entry.S b/roms/qboot/entry.S new file mode 100644 index 000000000..31d840bac --- /dev/null +++ b/roms/qboot/entry.S @@ -0,0 +1,138 @@ +/* + * Our pretty trivial BIOS emulation + */ + +#include "assembly.h" +#include "processor-flags.h" + + .org 0 + .code16gcc + +/* + * handy BIOS macros + */ + +/* If you change these macros, remember to update 'struct biosregs' */ +.macro SAVE_BIOSREGS + pushl %fs + pushl %es + pushl %ds + pushl %edi + pushl %esi + pushl %ebp + pushl %esp + pushl %edx + pushl %ecx + pushl %ebx + pushl %eax +.endm + +.macro RESTORE_BIOSREGS + popl %eax + popl %ebx + popl %ecx + popl %edx + popl %esp + popl %ebp + popl %esi + popl %edi + popl %ds + popl %es + popl %fs +.endm + +ENTRY(bios_irq) + pushw %ax + mov $0x20, %al + out %al, $0x20 + popw %ax + IRET +ENTRY_END(bios_irq) + +/* + * fake interrupt handler, nothing can be faster ever + */ +ENTRY(bios_intfake) + /* + * Set CF to indicate failure. We don't want callers to think that the + * interrupt handler succeeded and then treat the return values in + * registers as valid data. + */ + orb $X86_EFLAGS_CF, 0x4(%esp) + + IRET +ENTRY_END(bios_intfake) + +/* + * int 10 - video - service + */ +ENTRY(bios_int10) + andb $~X86_EFLAGS_CF, 0x4(%esp) + SAVE_BIOSREGS + + movl %esp, %eax + /* this is way easier than doing it in assembly */ + /* just push all the regs and jump to a C handler */ + call int10_handler + + RESTORE_BIOSREGS + + IRET +ENTRY_END(bios_int10) + +ENTRY(bios_int15) + andb $~X86_EFLAGS_CF, 0x4(%esp) + SAVE_BIOSREGS + + movl %esp, %eax + call int15_handler + + RESTORE_BIOSREGS + + IRET +ENTRY_END(bios_int15) + + .code32 +ENTRY(pcibios_entry) + clc + pushfl + SAVE_BIOSREGS + + movl %esp, %eax + call pcibios_handler + + RESTORE_BIOSREGS + popfl + lretl +ENTRY_END(pcibios_entry) + +ENTRY(bios32_entry) + pushfl + testl %ebx, %ebx /* BIOS32 service directory? */ + jnz 2f + cmp $0x49435024, %eax /* "$PCI"? */ + movb $0x80, %al /* service not present */ + jne 1f + xorl %ebx, %ebx /* fill in base/length/entry */ + movl $(1 << 20), %ecx + movl $pcibios_entry, %edx + movb $0x00, %al /* service present */ +1: + popfl + lretl +2: + movb $0x81, %al /* unimplemented function */ + popfl + lretl +ENTRY_END(bios32_entry) + +ENTRY(pic_base) + call 1f +2: + ret +1: + popl %eax + pushl %eax + subl $2b, %eax + ret /* return to 2b */ +ENTRY_END(pic_base) diff --git a/roms/qboot/flat.lds b/roms/qboot/flat.lds new file mode 100644 index 000000000..933ed60bb --- /dev/null +++ b/roms/qboot/flat.lds @@ -0,0 +1,28 @@ +OUTPUT_ARCH(i386) + +SECTIONS +{ + . = 1024K - 64K; + stext = .; + .text : { *(.text.startup) *(.text) *(.text.*) } + . = ALIGN(16); + .data : { *(.data) } + . = ALIGN(16); + .rodata : { *(.rodata) } + . = ALIGN(16); + .bss : { *(.bss) } + . = ALIGN(16); + edata = .; + . = 1024K - 128; + sinit = .; + .init : { + *(.init); + . = 128 - 16; + *(.resetvector); + . = 128; + } + einit = .; +} + +ENTRY(main) + diff --git a/roms/qboot/fw_cfg.c b/roms/qboot/fw_cfg.c new file mode 100644 index 000000000..4b920cffb --- /dev/null +++ b/roms/qboot/fw_cfg.c @@ -0,0 +1,315 @@ +#include "bios.h" +#include "e820.h" +#include "stdio.h" +#include "ioport.h" +#include "string.h" +#include "fw_cfg.h" +#include "bswap.h" +#include "linuxboot.h" +#include "memaccess.h" +#include "multiboot.h" +#include "benchmark.h" +#include "start_info.h" + +extern struct hvm_start_info start_info; + +struct fw_cfg_file { + uint32_t size; + uint16_t select; + uint16_t unused; + char name[56]; +}; + +static int version; +static int filecnt; +static struct fw_cfg_file *files; + +void fw_cfg_setup(void) +{ + int i, n; + + fw_cfg_select(FW_CFG_ID); + version = fw_cfg_readl_le(); + + fw_cfg_select(FW_CFG_FILE_DIR); + n = fw_cfg_readl_be(); + filecnt = n; + files = malloc_fseg(sizeof(files[0]) * n); + + fw_cfg_read(files, sizeof(files[0]) * n); + for (i = 0; i < n; i++) { + struct fw_cfg_file *f = &files[i]; + f->size = bswap32(f->size); + f->select = bswap16(f->select); + } +} + +int filenamecmp(const char *a, const struct fw_cfg_file *f) +{ + int n = sizeof(f->name); + const char *b = f->name; + while (*a == *b) { + if (*a == '\0') { + break; + } + if (--n == 0) { + return *a; + } + ++a, ++b; + } + return *a - *b; +} + +int fw_cfg_file_id(char *name) +{ + int i; + + for (i = 0; i < filecnt; i++) + if (!filenamecmp(name, &files[i])) + return i; + + return -1; +} + +uint32_t fw_cfg_file_size(int id) +{ + if (id == -1) + return 0; + return files[id].size; +} + +void fw_cfg_file_select(int id) +{ + fw_cfg_select(files[id].select); +} + +void fw_cfg_read_file(int id, void *buf, int len) +{ + fw_cfg_read_entry(files[id].select, buf, len); +} + +struct fw_cfg_dma_descriptor { + uint32_t control; + uint32_t length; + uint64_t address; +} __attribute__((packed)); + +void fw_cfg_dma(int control, void *buf, int len) +{ + volatile struct fw_cfg_dma_descriptor dma; + uint32_t dma_desc_addr; + + dma.control = bswap32(control); + dma.length = bswap32(len); + dma.address = bswap64((uintptr_t)buf); + + dma_desc_addr = (uint32_t)&dma; + outl(FW_CFG_DMA_ADDR_LOW, bswap32(dma_desc_addr)); + while (bswap32(dma.control) & ~FW_CFG_DMA_CTL_ERROR) { + asm(""); + } +} + +void fw_cfg_read(void *buf, int len) +{ + if (version & FW_CFG_VERSION_DMA) { + fw_cfg_dma(FW_CFG_DMA_CTL_READ, buf, len); + } else { + insb(buf, FW_CFG_DATA, len); + } +} + +void +fw_cfg_read_entry(int e, void *buf, int len) +{ + if (version & FW_CFG_VERSION_DMA) { + int control; + control = (e << 16); + control |= FW_CFG_DMA_CTL_SELECT; + control |= FW_CFG_DMA_CTL_READ; + fw_cfg_dma(control, buf, len); + } else { + fw_cfg_select(e); + insb(buf, FW_CFG_DATA, len); + } +} + +/* Multiboot trampoline. QEMU does the ELF parsing. */ + +static void boot_multiboot_from_fw_cfg(void) +{ + void *kernel_addr, *kernel_entry; + struct mb_info *mb; + struct mb_mmap_entry *mbmem; + int i; + uint32_t sz; + + fw_cfg_select(FW_CFG_KERNEL_SIZE); + sz = fw_cfg_readl_le(); + if (!sz) + panic(); + + fw_cfg_select(FW_CFG_KERNEL_ADDR); + kernel_addr = (void *) fw_cfg_readl_le(); + fw_cfg_read_entry(FW_CFG_KERNEL_DATA, kernel_addr, sz); + + fw_cfg_select(FW_CFG_INITRD_SIZE); + sz = fw_cfg_readl_le(); + if (!sz) + panic(); + + fw_cfg_select(FW_CFG_INITRD_ADDR); + mb = (struct mb_info *) fw_cfg_readl_le(); + fw_cfg_read_entry(FW_CFG_INITRD_DATA, mb, sz); + + mb->mem_lower = 639; + mb->mem_upper = (lowmem - 1048576) >> 10; + + mb->mmap_length = 0; + for (i = 0; i < e820->nr_map; i++) { + mbmem = (struct mb_mmap_entry *) (mb->mmap_addr + mb->mmap_length); + mbmem->size = sizeof(e820->map[i]); + mbmem->base_addr = e820->map[i].addr; + mbmem->length = e820->map[i].size; + mbmem->type = e820->map[i].type; + mb->mmap_length += sizeof(*mbmem); + } + +#ifdef BENCHMARK_HACK + /* Exit just before getting to vmlinuz, so that it is easy + * to time/profile the firmware. + */ + outb(LINUX_EXIT_PORT, LINUX_START_FWCFG); +#endif + + fw_cfg_select(FW_CFG_KERNEL_ENTRY); + kernel_entry = (void *) fw_cfg_readl_le(); + asm volatile("jmp *%2" : : "a" (0x2badb002), "b"(mb), "c"(kernel_entry)); + panic(); +} + +static void pvh_e820_setup() +{ + struct hvm_memmap_table_entry *pvh_e820p; + int i, pvh_e820_sz; + + pvh_e820_sz = sizeof(struct hvm_memmap_table_entry) * e820->nr_map; + + pvh_e820p = malloc(pvh_e820_sz); + memset(pvh_e820p, 0, pvh_e820_sz); + + for (i = 0; i < e820->nr_map; i++) { + pvh_e820p[i].addr = e820->map[i].addr; + pvh_e820p[i].size = e820->map[i].size; + pvh_e820p[i].type = e820->map[i].type; + } + start_info.memmap_paddr = (uintptr_t)pvh_e820p; + start_info.memmap_entries = e820->nr_map; +} + +static void boot_pvh_from_fw_cfg(void) +{ + void *kernel_entry; + uint32_t sz; + struct linuxboot_args args; + struct hvm_modlist_entry ramdisk_mod; + + start_info.magic = XEN_HVM_START_MAGIC_VALUE; + start_info.version = 1; + start_info.flags = 0; + start_info.nr_modules = 0; + start_info.reserved = 0; + + fw_cfg_select(FW_CFG_CMDLINE_SIZE); + args.cmdline_size = fw_cfg_readl_le(); + args.cmdline_addr = malloc(args.cmdline_size); + fw_cfg_read_entry(FW_CFG_CMDLINE_DATA, args.cmdline_addr, + args.cmdline_size); + start_info.cmdline_paddr = (uintptr_t)args.cmdline_addr; + + fw_cfg_select(FW_CFG_INITRD_SIZE); + args.initrd_size = fw_cfg_readl_le(); + if (args.initrd_size) { + fw_cfg_select(FW_CFG_INITRD_ADDR); + args.initrd_addr = (void *)fw_cfg_readl_le(); + + fw_cfg_read_entry(FW_CFG_INITRD_DATA, args.initrd_addr, + args.initrd_size); + + ramdisk_mod.paddr = (uintptr_t)args.initrd_addr; + ramdisk_mod.size = (uintptr_t)args.initrd_size; + + /* The first module is always ramdisk. */ + start_info.modlist_paddr = (uintptr_t)&ramdisk_mod; + start_info.nr_modules = 1; + } + + pvh_e820_setup(); + + fw_cfg_select(FW_CFG_KERNEL_SIZE); + sz = fw_cfg_readl_le(); + if (!sz) + panic(); + + fw_cfg_select(FW_CFG_KERNEL_ENTRY); + kernel_entry = (void *) fw_cfg_readl_le(); + +#ifdef BENCHMARK_HACK + /* Exit just before jumping to vmlinux, so that it is easy + * to time/profile the firmware. + */ + outb(LINUX_EXIT_PORT, LINUX_START_PVHBOOT); +#endif + asm volatile("jmp *%2" : : "a" (0x2badb002), + "b"(&start_info), "c"(kernel_entry)); + panic(); +} + +void boot_from_fwcfg(void) +{ + struct linuxboot_args args; + uint32_t kernel_size; + + fw_cfg_select(FW_CFG_CMDLINE_SIZE); + args.cmdline_size = fw_cfg_readl_le(); + fw_cfg_select(FW_CFG_INITRD_SIZE); + args.initrd_size = fw_cfg_readl_le(); + + /* QEMU has already split the real mode and protected mode + * parts. Recombine them in args.vmlinuz_size. + */ + fw_cfg_select(FW_CFG_KERNEL_SIZE); + kernel_size = fw_cfg_readl_le(); + fw_cfg_select(FW_CFG_SETUP_SIZE); + args.vmlinuz_size = kernel_size + fw_cfg_readl_le(); + + if (!args.vmlinuz_size) + return; + + fw_cfg_select(FW_CFG_SETUP_DATA); + fw_cfg_read(args.header, sizeof(args.header)); + + if (!parse_bzimage(&args)) { + uint8_t *header = args.header; + + if (ldl_p(header) == 0x464c457f) /* ELF magic */ + boot_pvh_from_fw_cfg(); + boot_multiboot_from_fw_cfg(); + } + + /* SETUP_DATA already selected */ + if (args.setup_size > sizeof(args.header)) + fw_cfg_read(args.setup_addr + sizeof(args.header), + args.setup_size - sizeof(args.header)); + + fw_cfg_select(FW_CFG_KERNEL_DATA); + fw_cfg_read_entry(FW_CFG_KERNEL_DATA, args.kernel_addr, kernel_size); + + fw_cfg_read_entry(FW_CFG_CMDLINE_DATA, args.cmdline_addr, args.cmdline_size); + + if (args.initrd_size) { + fw_cfg_read_entry(FW_CFG_INITRD_DATA, args.initrd_addr, args.initrd_size); + } + + boot_bzimage(&args); +} diff --git a/roms/qboot/hwsetup.c b/roms/qboot/hwsetup.c new file mode 100644 index 000000000..250213e5c --- /dev/null +++ b/roms/qboot/hwsetup.c @@ -0,0 +1,160 @@ +#include "bios.h" +#include "ioport.h" +#include "pci.h" +#include "string.h" + +// NOTE: this runs from ROM at 0xFFFF0000, so it is not possible to use any +// static data. + +#define PIIX_ISA_PIRQA_ROUT 0x60 +#define PIIX_PMBASE 0x40 +#define PIIX_PMREGMISC 0x80 +#define PIIX_SMBHSTBASE 0x90 +#define PIIX_SMBHSTCFG 0xd2 + +static void setup_piix(void) +{ + const int bdf = (1 << 3); + pci_config_writeb(bdf, PIIX_ISA_PIRQA_ROUT, 10); + pci_config_writeb(bdf, PIIX_ISA_PIRQA_ROUT+1, 10); + pci_config_writeb(bdf, PIIX_ISA_PIRQA_ROUT+2, 11); + pci_config_writeb(bdf, PIIX_ISA_PIRQA_ROUT+3, 11); +} + +static void setup_piix_pm(void) +{ + const int bdf = (1 << 3) | 3; + + pci_config_writel(bdf, PIIX_PMBASE, 0x601); + pci_config_writeb(bdf, PIIX_PMREGMISC, 0x01); + pci_config_writel(bdf, PIIX_SMBHSTBASE, 0x701); + pci_config_writeb(bdf, PIIX_SMBHSTCFG, 0x09); +} + +#define ICH9_LPC_PIRQA_ROUT 0x60 +#define ICH9_LPC_PIRQE_ROUT 0x68 +#define ICH9_LPC_PMBASE 0x40 +#define ICH9_LPC_ACPI_CTRL 0x44 + +static void setup_ich9(void) +{ + const int bdf = 0x1f << 3; + pci_config_writeb(bdf, ICH9_LPC_PIRQA_ROUT, 10); + pci_config_writeb(bdf, ICH9_LPC_PIRQA_ROUT+1, 10); + pci_config_writeb(bdf, ICH9_LPC_PIRQA_ROUT+2, 11); + pci_config_writeb(bdf, ICH9_LPC_PIRQA_ROUT+3, 11); + pci_config_writeb(bdf, ICH9_LPC_PIRQE_ROUT, 10); + pci_config_writeb(bdf, ICH9_LPC_PIRQE_ROUT+1, 10); + pci_config_writeb(bdf, ICH9_LPC_PIRQE_ROUT+2, 11); + pci_config_writeb(bdf, ICH9_LPC_PIRQE_ROUT+3, 11); +} + +static void setup_ich9_pm(void) +{ + const int bdf = 0x1f << 3; + pci_config_writel(bdf, ICH9_LPC_PMBASE, 0x601); + pci_config_writeb(bdf, ICH9_LPC_ACPI_CTRL, 0x80); +} + +#define I440FX_PAM0 0x59 +#define Q35_HOST_BRIDGE_PAM0 0x90 + +static void setup_pic(void) +{ + /* Send ICW1 (select OCW1 + will send ICW4) */ + outb(0x20, 0x11); + outb(0xa0, 0x11); + /* Send ICW2 (base irqs: 0x08-0x0f for irq0-7, 0x70-0x77 for irq8-15) */ + outb(0x21, 8); + outb(0xa1, 0x70); + /* Send ICW3 (cascaded pic ids) */ + outb(0x21, 0x04); + outb(0xa1, 0x02); + /* Send ICW4 (enable 8086 mode) */ + outb(0x21, 0x01); + outb(0xa1, 0x01); + /* Mask all irqs (except cascaded PIC2 irq) */ + outb(0x21, ~(1 << 2)); + outb(0xa1, ~0); + + /* Set ELCR to IRQs 10 and 11 */ + outb(0x4d0, 0); + outb(0x4d1, 0x0c); +} + +void setup_pam(int bdf, int pambase) +{ + int i; + for (i=0; i<6; i++) { + int pam = pambase + 1 + i; + pci_config_writeb(bdf, pam, 0x33); + } + + // Make ram from 0xf0000-0x100000 read-write + pci_config_writeb(bdf, pambase, 0x30); +} + +bool setup_hw(void) +{ + const int bdf = 0; + const uint8_t *bios_start = (void *)((uintptr_t)&stext + 0xfff00000); + const uint8_t *init_start = (void *)((uintptr_t)&sinit + 0xfff00000); + static volatile uint8_t rom_check; + int rom_check_value; + int pambase; + + uint32_t id = pci_config_readl(bdf, 0); + if (id == (PCI_VENDOR_ID_INTEL | (PCI_DEVICE_ID_INTEL_82441 << 16))) { + setup_piix(); + setup_piix_pm(); + pambase = I440FX_PAM0; + } else if (id == (PCI_VENDOR_ID_INTEL | (PCI_DEVICE_ID_INTEL_Q35_MCH << 16))) { + setup_ich9(); + setup_ich9_pm(); + pambase = Q35_HOST_BRIDGE_PAM0; + } else { + return false; + } + + // Make ram from 0xc0000-0xf0000 read-write + rom_check_value = rom_check; + rom_check = rom_check_value + 1; + if (rom_check == rom_check_value) + setup_pam(bdf, pambase); + + // Shadow BIOS; we're still running from 0xffff0000 + memcpy(&stext, bios_start, &edata - &stext); + memcpy(&sinit, init_start, &einit - &sinit); + + setup_pic(); + + return true; +} + +#define Q35_HOST_BRIDGE_PCIEXBAREN 1 +#define Q35_HOST_BRIDGE_PCIEXBAR 0x60 + +static void setup_q35_mmconfig(void) +{ + const int bdf = 0; + uint64_t addr = PCIE_MMCONFIG_BASE; + uint32_t upper = addr >> 32; + uint32_t lower = (addr & 0xffffffff) | Q35_HOST_BRIDGE_PCIEXBAREN; + + pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, 0); + pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR + 4, upper); + pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, lower); +} + +bool setup_mmconfig(void) +{ + const int bdf = 0; + uint32_t id = pci_config_readl(bdf, 0); + + if (id == (PCI_VENDOR_ID_INTEL | (PCI_DEVICE_ID_INTEL_Q35_MCH << 16))) { + setup_q35_mmconfig(); + return true; + } + + return false; +} diff --git a/roms/qboot/include/assembly.h b/roms/qboot/include/assembly.h new file mode 100644 index 000000000..dce958d7a --- /dev/null +++ b/roms/qboot/include/assembly.h @@ -0,0 +1,26 @@ +#ifndef ASSEMBLY_H_ +#define ASSEMBLY_H_ + +#define __ASSEMBLY__ + +#define __ALIGN .p2align 4, 0x90 +#define ENTRY(name) \ + __ALIGN; \ + .globl name; \ + name: + +#define GLOBAL(name) \ + .globl name; \ + name: + +#define ENTRY_END(name) GLOBAL(name##_end) +#define END(name) GLOBAL(name##_end) + +/* + * gas produces size override prefix with which + * we are unhappy, lets make it hardcoded for + * 16 bit mode + */ +#define IRET .byte 0xcf + +#endif /* ASSEMBLY_H_ */ diff --git a/roms/qboot/include/bios.h b/roms/qboot/include/bios.h new file mode 100644 index 000000000..7f8f67767 --- /dev/null +++ b/roms/qboot/include/bios.h @@ -0,0 +1,92 @@ +#ifndef BIOS_H_ +#define BIOS_H_ + +#include <stdint.h> +#include <stddef.h> +#include <stdbool.h> + +/* + * When interfacing with assembler code we need to be sure how + * arguments are passed in real mode. + */ +#define bioscall __attribute__((regparm(3))) + +#ifndef __ASSEMBLER__ + +struct biosregs { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t esp; + uint32_t ebp; + uint32_t esi; + uint32_t edi; + uint32_t ds; + uint32_t es; + uint32_t fs; + uint16_t ip; + uint16_t cs; + uint16_t eflags; +} __attribute__((packed)); + +/* + * BIOS32 is called via a far call, so eflags is pushed by our + * entry point and lies below CS:EIP. We do not include CS:EIP + * at all in this struct. + */ +struct bios32regs { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t esp; + uint32_t ebp; + uint32_t esi; + uint32_t edi; + uint32_t ds; + uint32_t es; + uint32_t fs; + uint32_t eflags; +} __attribute__((packed)); + +extern bioscall void int10_handler(struct biosregs *regs); +extern bioscall void int15_handler(struct biosregs *regs); +extern bioscall void e820_query_map(struct biosregs *regs); +extern bioscall void pcibios_handler(struct bios32regs *regs); + +extern void bios_intfake(void); +extern void bios_irq(void); +extern void bios_int10(void); +extern void bios_int15(void); +extern void bios32_entry(void); + +extern uint32_t pic_base(void); + +extern void setup_pci(void); +extern bool setup_hw(void); +extern bool setup_mmconfig(void); +extern void setup_mptable(void); +extern void extract_acpi(void); +extern void boot_from_fwcfg(void); + +extern uint8_t max_bus; +extern uint16_t e820_seg; +extern uint32_t lowmem; + +extern uint8_t stext; +extern uint8_t edata; +extern uint8_t sinit; +extern uint8_t einit; + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +static inline void __attribute__((noreturn)) panic(void) +{ + asm volatile("cli; hlt"); + for(;;); +} + +#endif + +#endif /* BIOS_H_ */ diff --git a/roms/qboot/include/bswap.h b/roms/qboot/include/bswap.h new file mode 100644 index 000000000..67b3c4f71 --- /dev/null +++ b/roms/qboot/include/bswap.h @@ -0,0 +1,34 @@ +#ifndef BSWAP_H +#define BSWAP_H 1 + +static inline uint16_t bswap16(uint16_t x) +{ + return __builtin_bswap16(x); +} + +static inline uint32_t bswap32(uint32_t x) +{ + return __builtin_bswap32(x); +} + +static inline uint64_t bswap64(uint64_t x) +{ + return __builtin_bswap64(x); +} + +static inline uint32_t ldl_le_p(const void *p) +{ + uint32_t val; + memcpy(&val, p, 4); + return val; +} + +static inline uint32_t ldl_be_p(const void *p) +{ + uint32_t val; + memcpy(&val, p, 4); + return bswap32(val); +} + + +#endif diff --git a/roms/qboot/include/const.h b/roms/qboot/include/const.h new file mode 100644 index 000000000..18f2350a2 --- /dev/null +++ b/roms/qboot/include/const.h @@ -0,0 +1,27 @@ +/* const.h: Macros for dealing with constants. */ + +#ifndef BIOS_CONST_H +#define BIOS_CONST_H + +/* Some constant macros are used in both assembler and + * C code. Therefore we cannot annotate them always with + * 'UL' and other type specifiers unilaterally. We + * use the following macros to deal with this. + * + * Similarly, _AT() will cast an expression with a type in C, but + * leave it unchanged in asm. + */ + +#ifdef __ASSEMBLY__ +#define _AC(X,Y) X +#define _AT(T,X) X +#else +#define __AC(X,Y) (X##Y) +#define _AC(X,Y) __AC(X,Y) +#define _AT(T,X) ((T)(X)) +#endif + +#define BITUL(x) (_AC(1,UL) << (x)) +#define BITULL(x) (_AC(1,ULL) << (x)) + +#endif /* BIOS_CONST_H */ diff --git a/roms/qboot/include/e820.h b/roms/qboot/include/e820.h new file mode 100644 index 000000000..09b8f0138 --- /dev/null +++ b/roms/qboot/include/e820.h @@ -0,0 +1,44 @@ +#ifndef BIOS_E820_H +#define BIOS_E820_H + +#define SMAP 0x534d4150 /* ASCII "SMAP" */ + +#define E820_RAM 1 +#define E820_RESERVED 2 +#define E820_ACPI 3 +#define E820_NVS 4 +#define E820_UNUSABLE 5 + + +/* + * reserved RAM used by kernel itself + * if CONFIG_INTEL_TXT is enabled, memory of this type will be + * included in the S3 integrity calculation and so should not include + * any memory that BIOS might alter over the S3 transition + */ +#define E820_RESERVED_KERN 128 + +struct e820entry { + uint64_t addr; /* start of memory segment */ + uint64_t size; /* size of memory segment */ + uint32_t type; /* type of memory segment */ +} __attribute__((packed)); + +struct e820map { + uint32_t nr_map; + struct e820entry map[]; +}; + +extern struct e820map *e820; + +#define ISA_START_ADDRESS 0xa0000 +#define ISA_END_ADDRESS 0x100000 + +#define BIOS_BEGIN 0x000a0000 +#define BIOS_END 0x00100000 + +#define BIOS_ROM_BASE 0xffe00000 +#define BIOS_ROM_END 0xffffffff + + +#endif /* BIOS_E820_H */ diff --git a/roms/qboot/include/fw_cfg.h b/roms/qboot/include/fw_cfg.h new file mode 100644 index 000000000..1ddfc1f7e --- /dev/null +++ b/roms/qboot/include/fw_cfg.h @@ -0,0 +1,120 @@ +#ifndef BIOS_FW_CFG_H +#define BIOS_FW_CFG_H 1 + +// List of QEMU fw_cfg entries. DO NOT ADD MORE. (All new content +// should be passed via the fw_cfg "file" interface.) +#define FW_CFG_SIGNATURE 0x00 +#define FW_CFG_ID 0x01 +#define FW_CFG_UUID 0x02 +#define FW_CFG_RAM_SIZE 0x03 +#define FW_CFG_NOGRAPHIC 0x04 +#define FW_CFG_NB_CPUS 0x05 +#define FW_CFG_MACHINE_ID 0x06 +#define FW_CFG_KERNEL_ADDR 0x07 +#define FW_CFG_KERNEL_SIZE 0x08 +#define FW_CFG_KERNEL_CMDLINE 0x09 +#define FW_CFG_INITRD_ADDR 0x0a +#define FW_CFG_INITRD_SIZE 0x0b +#define FW_CFG_BOOT_DEVICE 0x0c +#define FW_CFG_NUMA 0x0d +#define FW_CFG_BOOT_MENU 0x0e +#define FW_CFG_MAX_CPUS 0x0f +#define FW_CFG_KERNEL_ENTRY 0x10 +#define FW_CFG_KERNEL_DATA 0x11 +#define FW_CFG_INITRD_DATA 0x12 +#define FW_CFG_CMDLINE_ADDR 0x13 +#define FW_CFG_CMDLINE_SIZE 0x14 +#define FW_CFG_CMDLINE_DATA 0x15 +#define FW_CFG_SETUP_ADDR 0x16 +#define FW_CFG_SETUP_SIZE 0x17 +#define FW_CFG_SETUP_DATA 0x18 +#define FW_CFG_FILE_DIR 0x19 +#define FW_CFG_FILE_FIRST 0x20 +#define FW_CFG_ARCH_LOCAL 0x8000 +#define FW_CFG_ACPI_TABLES (FW_CFG_ARCH_LOCAL + 0) +#define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1) +#define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2) +#define FW_CFG_E820_TABLE (FW_CFG_ARCH_LOCAL + 3) + +#define FW_CFG_VERSION 0x01 +#define FW_CFG_VERSION_DMA 0x02 + +#define FW_CFG_DMA_CTL_ERROR 0x01 +#define FW_CFG_DMA_CTL_READ 0x02 +#define FW_CFG_DMA_CTL_SKIP 0x04 +#define FW_CFG_DMA_CTL_SELECT 0x08 + +#define FW_CFG_CTL 0x510 +#define FW_CFG_DATA 0x511 +#define FW_CFG_DMA_ADDR_HIGH 0x514 +#define FW_CFG_DMA_ADDR_LOW 0x518 + +#include "ioport.h" + +static inline void fw_cfg_select(uint16_t f) +{ + outw(FW_CFG_CTL, f); +} + +static inline uint32_t fw_cfg_readb(void) +{ + return inb(FW_CFG_DATA); +} + +static inline uint32_t fw_cfg_readw_be(void) +{ + uint32_t val; + + val = inb(FW_CFG_DATA); + val = (val << 8) | inb(FW_CFG_DATA); + return val; +} + +static inline uint32_t fw_cfg_readw_le(void) +{ + uint32_t val; + + val = inb(FW_CFG_DATA); + val = (inb(FW_CFG_DATA) << 8) | val; + return val; +} + +static inline uint32_t fw_cfg_readl_be(void) +{ + uint32_t val; + + val = inb(FW_CFG_DATA); + val = (val << 8) | inb(FW_CFG_DATA); + val = (val << 8) | inb(FW_CFG_DATA); + val = (val << 8) | inb(FW_CFG_DATA); + return val; +} + +static inline uint32_t fw_cfg_readl_le(void) +{ + uint32_t val; + + val = inb(FW_CFG_DATA); + val = (inb(FW_CFG_DATA) << 8) | val; + val = (inb(FW_CFG_DATA) << 16) | val; + val = (inb(FW_CFG_DATA) << 24) | val; + return val; +} + +static inline void fw_cfg_skip(int len) +{ + while (len--) + inb(FW_CFG_DATA); +} + +void fw_cfg_setup(void); +int fw_cfg_file_id(char *name); +uint32_t fw_cfg_file_size(int id); +void fw_cfg_file_select(int id); + +void fw_cfg_read(void *buf, int len); +void fw_cfg_read_entry(int e, void *buf, int len); +void fw_cfg_dma(int control, void *buf, int len); +void fw_cfg_read_file(int e, void *buf, int len); + +#endif diff --git a/roms/qboot/include/ioport.h b/roms/qboot/include/ioport.h new file mode 100644 index 000000000..08a8dbd9b --- /dev/null +++ b/roms/qboot/include/ioport.h @@ -0,0 +1,50 @@ +#ifndef BIOS_IOPORT_H +#define BIOS_IOPORT_H 1 + +static inline void outsb(unsigned short port, void *buf, int len) +{ + asm volatile("rep outsb %%ds:(%0), %3" : "=S" (buf), "=c" (len) : "m"(buf), "Nd"(port), "0" (buf), "1" (len)); +} + +static inline void insb(void *buf, unsigned short port, int len) +{ + asm volatile("rep insb %3, %%es:(%0)" : "=D" (buf), "=c" (len), "=m"(buf) : "Nd"(port), "0" (buf), "1" (len)); +} + +static inline unsigned char inb(unsigned short port) +{ + unsigned char val; + asm volatile("inb %1, %0" : "=a"(val) : "Nd"(port)); + return val; +} + +static inline unsigned short inw(unsigned short port) +{ + unsigned short val; + asm volatile("inw %1, %0" : "=a"(val) : "Nd"(port)); + return val; +} + +static inline unsigned inl(unsigned short port) +{ + unsigned val; + asm volatile("inl %1, %0" : "=a"(val) : "Nd"(port)); + return val; +} + +static inline void outb(unsigned short port, unsigned char val) +{ + asm volatile("outb %0, %1" : : "a"(val), "Nd"(port)); +} + +static inline void outw(unsigned short port, unsigned short val) +{ + asm volatile("outw %0, %1" : : "a"(val), "Nd"(port)); +} + +static inline void outl(unsigned short port, unsigned val) +{ + asm volatile("outl %0, %1" : : "a"(val), "Nd"(port)); +} + +#endif diff --git a/roms/qboot/include/linuxboot.h b/roms/qboot/include/linuxboot.h new file mode 100644 index 000000000..6e865f0f2 --- /dev/null +++ b/roms/qboot/include/linuxboot.h @@ -0,0 +1,19 @@ +#ifndef BIOS_LINUXBOOT_H +#define BIOS_LINUXBOOT_H 1 + +#include <stdbool.h> + +struct linuxboot_args { + /* Output */ + void *setup_addr, *cmdline_addr, *kernel_addr, *initrd_addr; + uint32_t setup_size, kernel_size; + + /* Input */ + uint32_t cmdline_size, vmlinuz_size, initrd_size; + uint8_t header[8192]; +}; + +bool parse_bzimage(struct linuxboot_args *args); +void boot_bzimage(struct linuxboot_args *args); + +#endif diff --git a/roms/qboot/include/memaccess.h b/roms/qboot/include/memaccess.h new file mode 100644 index 000000000..900b47e63 --- /dev/null +++ b/roms/qboot/include/memaccess.h @@ -0,0 +1,30 @@ +#ifndef MEMACCESS_H_ +#define MEMACCESS_H_ + +#include "string.h" + +static inline uint16_t lduw_p(void *p) +{ + uint16_t val; + memcpy(&val, p, 2); + return val; +} + +static inline uint32_t ldl_p(void *p) +{ + uint32_t val; + memcpy(&val, p, 4); + return val; +} + +static inline void stw_p(void *p, uint16_t val) +{ + memcpy(p, &val, 2); +} + +static inline void stl_p(void *p, uint32_t val) +{ + memcpy(p, &val, 4); +} + +#endif /* MEMACCESS_H_ */ diff --git a/roms/qboot/include/mpspec_def.h b/roms/qboot/include/mpspec_def.h new file mode 100644 index 000000000..6fb923a34 --- /dev/null +++ b/roms/qboot/include/mpspec_def.h @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_X86_MPSPEC_DEF_H +#define _ASM_X86_MPSPEC_DEF_H + +/* + * Structure definitions for SMP machines following the + * Intel Multiprocessing Specification 1.1 and 1.4. + */ + +/* + * This tag identifies where the SMP configuration + * information is. + */ + +#define SMP_MAGIC_IDENT (('_'<<24) | ('P'<<16) | ('M'<<8) | '_') + +#ifdef CONFIG_X86_32 +# define MAX_MPC_ENTRY 1024 +#endif + +/* Intel MP Floating Pointer Structure */ +struct mpf_intel { + char signature[4]; /* "_MP_" */ + unsigned int physptr; /* Configuration table address */ + unsigned char length; /* Our length (paragraphs) */ + unsigned char specification; /* Specification version */ + unsigned char checksum; /* Checksum (makes sum 0) */ + unsigned char feature1; /* Standard or configuration ? */ + unsigned char feature2; /* Bit7 set for IMCR|PIC */ + unsigned char feature3; /* Unused (0) */ + unsigned char feature4; /* Unused (0) */ + unsigned char feature5; /* Unused (0) */ +}; + +#define MPC_SIGNATURE "PCMP" + +struct mpc_table { + char signature[4]; + unsigned short length; /* Size of table */ + char spec; /* 0x01 */ + char checksum; + char oem[8]; + char productid[12]; + unsigned int oemptr; /* 0 if not present */ + unsigned short oemsize; /* 0 if not present */ + unsigned short oemcount; + unsigned int lapic; /* APIC address */ + unsigned int reserved; +}; + +/* Followed by entries */ + +#define MP_PROCESSOR 0 +#define MP_BUS 1 +#define MP_IOAPIC 2 +#define MP_INTSRC 3 +#define MP_LINTSRC 4 +/* Used by IBM NUMA-Q to describe node locality */ +#define MP_TRANSLATION 192 + +#define CPU_ENABLED 1 /* Processor is available */ +#define CPU_BOOTPROCESSOR 2 /* Processor is the boot CPU */ + +#define CPU_STEPPING_MASK 0x000F +#define CPU_MODEL_MASK 0x00F0 +#define CPU_FAMILY_MASK 0x0F00 + +struct mpc_cpu { + unsigned char type; + unsigned char apicid; /* Local APIC number */ + unsigned char apicver; /* Its versions */ + unsigned char cpuflag; + unsigned int cpufeature; + unsigned int featureflag; /* CPUID feature value */ + unsigned int reserved[2]; +}; + +struct mpc_bus { + unsigned char type; + unsigned char busid; + unsigned char bustype[6]; +}; + +/* List of Bus Type string values, Intel MP Spec. */ +#define BUSTYPE_EISA "EISA" +#define BUSTYPE_ISA "ISA" +#define BUSTYPE_INTERN "INTERN" /* Internal BUS */ +#define BUSTYPE_MCA "MCA" /* Obsolete */ +#define BUSTYPE_VL "VL" /* Local bus */ +#define BUSTYPE_PCI "PCI" +#define BUSTYPE_PCMCIA "PCMCIA" +#define BUSTYPE_CBUS "CBUS" +#define BUSTYPE_CBUSII "CBUSII" +#define BUSTYPE_FUTURE "FUTURE" +#define BUSTYPE_MBI "MBI" +#define BUSTYPE_MBII "MBII" +#define BUSTYPE_MPI "MPI" +#define BUSTYPE_MPSA "MPSA" +#define BUSTYPE_NUBUS "NUBUS" +#define BUSTYPE_TC "TC" +#define BUSTYPE_VME "VME" +#define BUSTYPE_XPRESS "XPRESS" + +#define MPC_APIC_USABLE 0x01 + +struct mpc_ioapic { + unsigned char type; + unsigned char apicid; + unsigned char apicver; + unsigned char flags; + unsigned int apicaddr; +}; + +struct mpc_intsrc { + unsigned char type; + unsigned char irqtype; + unsigned short irqflag; + unsigned char srcbus; + unsigned char srcbusirq; + unsigned char dstapic; + unsigned char dstirq; +}; + +enum mp_irq_source_types { + mp_INT = 0, + mp_NMI = 1, + mp_SMI = 2, + mp_ExtINT = 3 +}; + +#define MP_IRQPOL_DEFAULT 0x0 +#define MP_IRQPOL_ACTIVE_HIGH 0x1 +#define MP_IRQPOL_RESERVED 0x2 +#define MP_IRQPOL_ACTIVE_LOW 0x3 +#define MP_IRQPOL_MASK 0x3 + +#define MP_IRQTRIG_DEFAULT 0x0 +#define MP_IRQTRIG_EDGE 0x4 +#define MP_IRQTRIG_RESERVED 0x8 +#define MP_IRQTRIG_LEVEL 0xc +#define MP_IRQTRIG_MASK 0xc + +#define MP_APIC_ALL 0xFF + +struct mpc_lintsrc { + unsigned char type; + unsigned char irqtype; + unsigned short irqflag; + unsigned char srcbusid; + unsigned char srcbusirq; + unsigned char destapic; + unsigned char destapiclint; +}; + +#define MPC_OEM_SIGNATURE "_OEM" + +struct mpc_oemtable { + char signature[4]; + unsigned short length; /* Size of table */ + char rev; /* 0x01 */ + char checksum; + char mpc[8]; +}; + +/* + * Default configurations + * + * 1 2 CPU ISA 82489DX + * 2 2 CPU EISA 82489DX neither IRQ 0 timer nor IRQ 13 DMA chaining + * 3 2 CPU EISA 82489DX + * 4 2 CPU MCA 82489DX + * 5 2 CPU ISA+PCI + * 6 2 CPU EISA+PCI + * 7 2 CPU MCA+PCI + */ + +enum mp_bustype { + MP_BUS_ISA = 1, + MP_BUS_EISA, + MP_BUS_PCI, +}; +#endif /* _ASM_X86_MPSPEC_DEF_H */ diff --git a/roms/qboot/include/multiboot.h b/roms/qboot/include/multiboot.h new file mode 100644 index 000000000..d3d4528f5 --- /dev/null +++ b/roms/qboot/include/multiboot.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013 Kevin Wolf <kwolf@redhat.com> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MULTIBOOT_H +#define MULTIBOOT_H + +#include <stdint.h> + +struct mb_info { + uint32_t flags; + uint32_t mem_lower; + uint32_t mem_upper; + uint32_t boot_device; + uint32_t cmdline; + uint32_t mods_count; + uint32_t mods_addr; + char syms[16]; + uint32_t mmap_length; + uint32_t mmap_addr; + uint32_t drives_length; + uint32_t drives_addr; + uint32_t config_table; + uint32_t boot_loader_name; + uint32_t apm_table; + uint32_t vbe_control_info; + uint32_t vbe_mode_info; + uint16_t vbe_mode; + uint16_t vbe_interface_seg; + uint16_t vbe_interface_off; + uint16_t vbe_interface_len; +} __attribute__((packed)); + +struct mb_module { + uint32_t mod_start; + uint32_t mod_end; + uint32_t string; + uint32_t reserved; +} __attribute__((packed)); + +struct mb_mmap_entry { + uint32_t size; + uint64_t base_addr; + uint64_t length; + uint32_t type; +} __attribute__((packed)); + +#endif diff --git a/roms/qboot/include/pci.h b/roms/qboot/include/pci.h new file mode 100644 index 000000000..7f40f3833 --- /dev/null +++ b/roms/qboot/include/pci.h @@ -0,0 +1,78 @@ +#ifndef BIOS_PCI_H +#define BIOS_PCI_H + +#include "ioport.h" + +static inline void pci_config_writel(uint16_t bdf, uint32_t addr, uint32_t val) +{ + outl(0xcf8, 0x80000000 | (bdf << 8) | (addr & 0xfc)); + outl(0xcfc, val); +} + +static inline void pci_config_writew(uint16_t bdf, uint32_t addr, uint16_t val) +{ + outl(0xcf8, 0x80000000 | (bdf << 8) | (addr & 0xfc)); + outw(0xcfc | (addr & 2), val); +} + +static inline void pci_config_writeb(uint16_t bdf, uint32_t addr, uint8_t val) +{ + outl(0xcf8, 0x80000000 | (bdf << 8) | (addr & 0xfc)); + outb(0xcfc | (addr & 3), val); +} + +static inline uint32_t pci_config_readl(uint16_t bdf, uint32_t addr) +{ + outl(0xcf8, 0x80000000 | (bdf << 8) | (addr & 0xfc)); + return inl(0xcfc); +} + +static inline uint16_t pci_config_readw(uint16_t bdf, uint32_t addr) +{ + outl(0xcf8, 0x80000000 | (bdf << 8) | (addr & 0xfc)); + return inw(0xcfc | (addr & 2)); +} + +static inline uint8_t pci_config_readb(uint16_t bdf, uint32_t addr) +{ + outl(0xcf8, 0x80000000 | (bdf << 8) | (addr & 0xfc)); + return inb(0xcfc | (addr & 3)); +} + +#define PCI_VENDOR_ID 0x00 +#define PCI_DEVICE_ID 0x02 +#define PCI_COMMAND 0x04 +#define PCI_CLASS_DEVICE 0x0a +#define PCI_HEADER_TYPE 0x0e +#define PCI_PRIMARY_BUS 0x18 +#define PCI_SECONDARY_BUS 0x19 +#define PCI_SUBORDINATE_BUS 0x1a +#define PCI_INTERRUPT_LINE 0x3c +#define PCI_INTERRUPT_PIN 0x3d +#define PCI_BRIDGE_CONTROL 0x3e + +/* PCI_COMMAND */ +#define PCI_COMMAND_DIS_INTX 0x400 + +/* PCI_CLASS_DEVICE */ +#define PCI_CLASS_STORAGE_IDE 0x0101 +#define PCI_CLASS_BRIDGE_PCI 0x0604 + +/* PCI_HEADER_TYPE */ +#define PCI_HEADER_TYPE_BRIDGE 1 +#define PCI_HEADER_TYPE_MULTI_FUNCTION 0x80 + +/* PCI_BRIDGE_CONTROL */ +#define PCI_BRIDGE_CTL_SERR 0x02 + +/* PCI_VENDOR_ID / PCI_DEVICE_ID */ +#define PCI_VENDOR_ID_INTEL 0x8086 +#define PCI_DEVICE_ID_INTEL_82441 0x1237 +#define PCI_DEVICE_ID_INTEL_Q35_MCH 0x29c0 +#define PCI_DEVICE_ID_INTEL_82371SB_1 0x7010 +#define PCI_DEVICE_ID_INTEL_82371AB 0x7111 + +#define PCIE_MMCONFIG_BASE 0xb0000000 +#define PCIE_MMCONFIG_SIZE (256 * 1024 * 1024) + +#endif diff --git a/roms/qboot/include/processor-flags.h b/roms/qboot/include/processor-flags.h new file mode 100644 index 000000000..d56cc83a2 --- /dev/null +++ b/roms/qboot/include/processor-flags.h @@ -0,0 +1,153 @@ +#ifndef BIOS_X86_PROCESSOR_FLAGS_H +#define BIOS_X86_PROCESSOR_FLAGS_H +/* Various flags defined: can be included from assembler. */ + +#include "const.h" + +/* + * EFLAGS bits + */ +#define X86_EFLAGS_CF_BIT 0 /* Carry Flag */ +#define X86_EFLAGS_CF BITUL(X86_EFLAGS_CF_BIT) +#define X86_EFLAGS_FIXED_BIT 1 /* Bit 1 - always on */ +#define X86_EFLAGS_FIXED BITUL(X86_EFLAGS_FIXED_BIT) +#define X86_EFLAGS_PF_BIT 2 /* Parity Flag */ +#define X86_EFLAGS_PF BITUL(X86_EFLAGS_PF_BIT) +#define X86_EFLAGS_AF_BIT 4 /* Auxiliary carry Flag */ +#define X86_EFLAGS_AF BITUL(X86_EFLAGS_AF_BIT) +#define X86_EFLAGS_ZF_BIT 6 /* Zero Flag */ +#define X86_EFLAGS_ZF BITUL(X86_EFLAGS_ZF_BIT) +#define X86_EFLAGS_SF_BIT 7 /* Sign Flag */ +#define X86_EFLAGS_SF BITUL(X86_EFLAGS_SF_BIT) +#define X86_EFLAGS_TF_BIT 8 /* Trap Flag */ +#define X86_EFLAGS_TF BITUL(X86_EFLAGS_TF_BIT) +#define X86_EFLAGS_IF_BIT 9 /* Interrupt Flag */ +#define X86_EFLAGS_IF BITUL(X86_EFLAGS_IF_BIT) +#define X86_EFLAGS_DF_BIT 10 /* Direction Flag */ +#define X86_EFLAGS_DF BITUL(X86_EFLAGS_DF_BIT) +#define X86_EFLAGS_OF_BIT 11 /* Overflow Flag */ +#define X86_EFLAGS_OF BITUL(X86_EFLAGS_OF_BIT) +#define X86_EFLAGS_IOPL_BIT 12 /* I/O Privilege Level (2 bits) */ +#define X86_EFLAGS_IOPL (_AC(3,UL) << X86_EFLAGS_IOPL_BIT) +#define X86_EFLAGS_NT_BIT 14 /* Nested Task */ +#define X86_EFLAGS_NT BITUL(X86_EFLAGS_NT_BIT) +#define X86_EFLAGS_RF_BIT 16 /* Resume Flag */ +#define X86_EFLAGS_RF BITUL(X86_EFLAGS_RF_BIT) +#define X86_EFLAGS_VM_BIT 17 /* Virtual Mode */ +#define X86_EFLAGS_VM BITUL(X86_EFLAGS_VM_BIT) +#define X86_EFLAGS_AC_BIT 18 /* Alignment Check/Access Control */ +#define X86_EFLAGS_AC BITUL(X86_EFLAGS_AC_BIT) +#define X86_EFLAGS_AC_BIT 18 /* Alignment Check/Access Control */ +#define X86_EFLAGS_AC BITUL(X86_EFLAGS_AC_BIT) +#define X86_EFLAGS_VIF_BIT 19 /* Virtual Interrupt Flag */ +#define X86_EFLAGS_VIF BITUL(X86_EFLAGS_VIF_BIT) +#define X86_EFLAGS_VIP_BIT 20 /* Virtual Interrupt Pending */ +#define X86_EFLAGS_VIP BITUL(X86_EFLAGS_VIP_BIT) +#define X86_EFLAGS_ID_BIT 21 /* CPUID detection */ +#define X86_EFLAGS_ID BITUL(X86_EFLAGS_ID_BIT) + +/* + * Basic CPU control in CR0 + */ +#define X86_CR0_PE_BIT 0 /* Protection Enable */ +#define X86_CR0_PE BITUL(X86_CR0_PE_BIT) +#define X86_CR0_MP_BIT 1 /* Monitor Coprocessor */ +#define X86_CR0_MP BITUL(X86_CR0_MP_BIT) +#define X86_CR0_EM_BIT 2 /* Emulation */ +#define X86_CR0_EM BITUL(X86_CR0_EM_BIT) +#define X86_CR0_TS_BIT 3 /* Task Switched */ +#define X86_CR0_TS BITUL(X86_CR0_TS_BIT) +#define X86_CR0_ET_BIT 4 /* Extension Type */ +#define X86_CR0_ET BITUL(X86_CR0_ET_BIT) +#define X86_CR0_NE_BIT 5 /* Numeric Error */ +#define X86_CR0_NE BITUL(X86_CR0_NE_BIT) +#define X86_CR0_WP_BIT 16 /* Write Protect */ +#define X86_CR0_WP BITUL(X86_CR0_WP_BIT) +#define X86_CR0_AM_BIT 18 /* Alignment Mask */ +#define X86_CR0_AM BITUL(X86_CR0_AM_BIT) +#define X86_CR0_NW_BIT 29 /* Not Write-through */ +#define X86_CR0_NW BITUL(X86_CR0_NW_BIT) +#define X86_CR0_CD_BIT 30 /* Cache Disable */ +#define X86_CR0_CD BITUL(X86_CR0_CD_BIT) +#define X86_CR0_PG_BIT 31 /* Paging */ +#define X86_CR0_PG BITUL(X86_CR0_PG_BIT) + +/* + * Paging options in CR3 + */ +#define X86_CR3_PWT_BIT 3 /* Page Write Through */ +#define X86_CR3_PWT BITUL(X86_CR3_PWT_BIT) +#define X86_CR3_PCD_BIT 4 /* Page Cache Disable */ +#define X86_CR3_PCD BITUL(X86_CR3_PCD_BIT) +#define X86_CR3_PCID_MASK _AC(0x00000fff,UL) /* PCID Mask */ + +/* + * Intel CPU features in CR4 + */ +#define X86_CR4_VME_BIT 0 /* enable vm86 extensions */ +#define X86_CR4_VME BITUL(X86_CR4_VME_BIT) +#define X86_CR4_PVI_BIT 1 /* virtual interrupts flag enable */ +#define X86_CR4_PVI BITUL(X86_CR4_PVI_BIT) +#define X86_CR4_TSD_BIT 2 /* disable time stamp at ipl 3 */ +#define X86_CR4_TSD BITUL(X86_CR4_TSD_BIT) +#define X86_CR4_DE_BIT 3 /* enable debugging extensions */ +#define X86_CR4_DE BITUL(X86_CR4_DE_BIT) +#define X86_CR4_PSE_BIT 4 /* enable page size extensions */ +#define X86_CR4_PSE BITUL(X86_CR4_PSE_BIT) +#define X86_CR4_PAE_BIT 5 /* enable physical address extensions */ +#define X86_CR4_PAE BITUL(X86_CR4_PAE_BIT) +#define X86_CR4_MCE_BIT 6 /* Machine check enable */ +#define X86_CR4_MCE BITUL(X86_CR4_MCE_BIT) +#define X86_CR4_PGE_BIT 7 /* enable global pages */ +#define X86_CR4_PGE BITUL(X86_CR4_PGE_BIT) +#define X86_CR4_PCE_BIT 8 /* enable performance counters at ipl 3 */ +#define X86_CR4_PCE BITUL(X86_CR4_PCE_BIT) +#define X86_CR4_OSFXSR_BIT 9 /* enable fast FPU save and restore */ +#define X86_CR4_OSFXSR BITUL(X86_CR4_OSFXSR_BIT) +#define X86_CR4_OSXMMEXCPT_BIT 10 /* enable unmasked SSE exceptions */ +#define X86_CR4_OSXMMEXCPT BITUL(X86_CR4_OSXMMEXCPT_BIT) +#define X86_CR4_VMXE_BIT 13 /* enable VMX virtualization */ +#define X86_CR4_VMXE BITUL(X86_CR4_VMXE_BIT) +#define X86_CR4_SMXE_BIT 14 /* enable safer mode (TXT) */ +#define X86_CR4_SMXE BITUL(X86_CR4_SMXE_BIT) +#define X86_CR4_FSGSBASE_BIT 16 /* enable RDWRFSGS support */ +#define X86_CR4_FSGSBASE BITUL(X86_CR4_FSGSBASE_BIT) +#define X86_CR4_PCIDE_BIT 17 /* enable PCID support */ +#define X86_CR4_PCIDE BITUL(X86_CR4_PCIDE_BIT) +#define X86_CR4_OSXSAVE_BIT 18 /* enable xsave and xrestore */ +#define X86_CR4_OSXSAVE BITUL(X86_CR4_OSXSAVE_BIT) +#define X86_CR4_SMEP_BIT 20 /* enable SMEP support */ +#define X86_CR4_SMEP BITUL(X86_CR4_SMEP_BIT) +#define X86_CR4_SMAP_BIT 21 /* enable SMAP support */ +#define X86_CR4_SMAP BITUL(X86_CR4_SMAP_BIT) + +/* + * x86-64 Task Priority Register, CR8 + */ +#define X86_CR8_TPR _AC(0x0000000f,UL) /* task priority register */ + +/* + * AMD and Transmeta use MSRs for configuration; see <asm/msr-index.h> + */ + +/* + * NSC/Cyrix CPU configuration register indexes + */ +#define CX86_PCR0 0x20 +#define CX86_GCR 0xb8 +#define CX86_CCR0 0xc0 +#define CX86_CCR1 0xc1 +#define CX86_CCR2 0xc2 +#define CX86_CCR3 0xc3 +#define CX86_CCR4 0xe8 +#define CX86_CCR5 0xe9 +#define CX86_CCR6 0xea +#define CX86_CCR7 0xeb +#define CX86_PCR1 0xf0 +#define CX86_DIR0 0xfe +#define CX86_DIR1 0xff +#define CX86_ARR_BASE 0xc4 +#define CX86_RCR_BASE 0xdc + + +#endif /* BIOS_X86_PROCESSOR_FLAGS_H */ diff --git a/roms/qboot/include/segment.h b/roms/qboot/include/segment.h new file mode 100644 index 000000000..18afb5310 --- /dev/null +++ b/roms/qboot/include/segment.h @@ -0,0 +1,19 @@ +#ifndef BIOS_SEGMENT_H +#define BIOS_SEGMENT_H + +static inline uint32_t segment_to_flat(uint16_t selector, uint16_t offset) +{ + return ((uint32_t)selector << 4) + (uint32_t) offset; +} + +static inline uint16_t flat_to_seg16(uint32_t address) +{ + return (address >> 4) & 0xf000; +} + +static inline uint16_t flat_to_off16(uint32_t address) +{ + return address & 65535; +} + +#endif /* KVM_SEGMENT_H */ diff --git a/roms/qboot/include/smbios.h b/roms/qboot/include/smbios.h new file mode 100644 index 000000000..ec033cec2 --- /dev/null +++ b/roms/qboot/include/smbios.h @@ -0,0 +1 @@ +void extract_smbios(void); diff --git a/roms/qboot/include/start_info.h b/roms/qboot/include/start_info.h new file mode 100644 index 000000000..348779eb1 --- /dev/null +++ b/roms/qboot/include/start_info.h @@ -0,0 +1,146 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2016, Citrix Systems, Inc. + */ + +#ifndef __XEN_PUBLIC_ARCH_X86_HVM_START_INFO_H__ +#define __XEN_PUBLIC_ARCH_X86_HVM_START_INFO_H__ + +/* + * Start of day structure passed to PVH guests and to HVM guests in %ebx. + * + * NOTE: nothing will be loaded at physical address 0, so a 0 value in any + * of the address fields should be treated as not present. + * + * 0 +----------------+ + * | magic | Contains the magic value XEN_HVM_START_MAGIC_VALUE + * | | ("xEn3" with the 0x80 bit of the "E" set). + * 4 +----------------+ + * | version | Version of this structure. Current version is 1. New + * | | versions are guaranteed to be backwards-compatible. + * 8 +----------------+ + * | flags | SIF_xxx flags. + * 12 +----------------+ + * | nr_modules | Number of modules passed to the kernel. + * 16 +----------------+ + * | modlist_paddr | Physical address of an array of modules + * | | (layout of the structure below). + * 24 +----------------+ + * | cmdline_paddr | Physical address of the command line, + * | | a zero-terminated ASCII string. + * 32 +----------------+ + * | rsdp_paddr | Physical address of the RSDP ACPI data structure. + * 40 +----------------+ + * | memmap_paddr | Physical address of the (optional) memory map. Only + * | | present in version 1 and newer of the structure. + * 48 +----------------+ + * | memmap_entries | Number of entries in the memory map table. Only + * | | present in version 1 and newer of the structure. + * | | Zero if there is no memory map being provided. + * 52 +----------------+ + * | reserved | Version 1 and newer only. + * 56 +----------------+ + * + * The layout of each entry in the module structure is the following: + * + * 0 +----------------+ + * | paddr | Physical address of the module. + * 8 +----------------+ + * | size | Size of the module in bytes. + * 16 +----------------+ + * | cmdline_paddr | Physical address of the command line, + * | | a zero-terminated ASCII string. + * 24 +----------------+ + * | reserved | + * 32 +----------------+ + * + * The layout of each entry in the memory map table is as follows: + * + * 0 +----------------+ + * | addr | Base address + * 8 +----------------+ + * | size | Size of mapping in bytes + * 16 +----------------+ + * | type | Type of mapping as defined between the hypervisor + * | | and guest it's starting. E820_TYPE_xxx, for example. + * 20 +----------------| + * | reserved | + * 24 +----------------+ + * + * The address and sizes are always a 64bit little endian unsigned integer. + * + * NB: Xen on x86 will always try to place all the data below the 4GiB + * boundary. + * + * Version numbers of the hvm_start_info structure have evolved like this: + * + * Version 0: + * + * Version 1: Added the memmap_paddr/memmap_entries fields (plus 4 bytes of + * padding) to the end of the hvm_start_info struct. These new + * fields can be used to pass a memory map to the guest. The + * memory map is optional and so guests that understand version 1 + * of the structure must check that memmap_entries is non-zero + * before trying to read the memory map. + */ +#define XEN_HVM_START_MAGIC_VALUE 0x336ec578 + +/* + * C representation of the x86/HVM start info layout. + * + * The canonical definition of this layout is above, this is just a way to + * represent the layout described there using C types. + */ +struct hvm_start_info { + uint32_t magic; /* Contains the magic value 0x336ec578 */ + /* ("xEn3" with the 0x80 bit of the "E" set).*/ + uint32_t version; /* Version of this structure. */ + uint32_t flags; /* SIF_xxx flags. */ + uint32_t nr_modules; /* Number of modules passed to the kernel. */ + uint64_t modlist_paddr; /* Physical address of an array of */ + /* hvm_modlist_entry. */ + uint64_t cmdline_paddr; /* Physical address of the command line. */ + uint64_t rsdp_paddr; /* Physical address of the RSDP ACPI data */ + /* structure. */ + uint64_t memmap_paddr; /* Physical address of an array of */ + /* hvm_memmap_table_entry. Only present in */ + /* version 1 and newer of the structure */ + uint32_t memmap_entries; /* Number of entries in the memmap table. */ + /* Only present in version 1 and newer of */ + /* the structure. Value will be zero if */ + /* there is no memory map being provided. */ + uint32_t reserved; +}; + +struct hvm_modlist_entry { + uint64_t paddr; /* Physical address of the module. */ + uint64_t size; /* Size of the module in bytes. */ + uint64_t cmdline_paddr; /* Physical address of the command line. */ + uint64_t reserved; +}; + +struct hvm_memmap_table_entry { + uint64_t addr; /* Base address of the memory region */ + uint64_t size; /* Size of the memory region in bytes */ + uint32_t type; /* Mapping type */ + uint32_t reserved; +}; + +#endif /* __XEN_PUBLIC_ARCH_X86_HVM_START_INFO_H__ */ diff --git a/roms/qboot/include/stdio.h b/roms/qboot/include/stdio.h new file mode 100644 index 000000000..c15493340 --- /dev/null +++ b/roms/qboot/include/stdio.h @@ -0,0 +1,11 @@ +#ifndef BIOS_STDIO_H +#define BIOS_STDIO_H 1 + +#include <stdarg.h> + +extern int puts(const char *s); +extern int printf(const char *fmt, ...); +extern int snprintf(char *buf, int size, const char *fmt, ...); +extern int vsnprintf(char *buf, int size, const char *fmt, va_list va); + +#endif diff --git a/roms/qboot/include/stdlib.h b/roms/qboot/include/stdlib.h new file mode 100644 index 000000000..8de2f316e --- /dev/null +++ b/roms/qboot/include/stdlib.h @@ -0,0 +1,6 @@ +#ifndef BIOS_STDLIB_H +#define BIOS_STDLIB_H 1 + +extern long atol(const char *ptr); + +#endif diff --git a/roms/qboot/include/string.h b/roms/qboot/include/string.h new file mode 100644 index 000000000..39decfab1 --- /dev/null +++ b/roms/qboot/include/string.h @@ -0,0 +1,40 @@ +#ifndef BIOS_STRING_H +#define BIOS_STRING_H + +#include <stddef.h> +#include <stdint.h> + +unsigned long strlen(const char *buf); +char *strcat(char *dest, const char *src); +char *strcpy(char *dest, const char *src); +int strcmp(const char *a, const char *b); +char *strchr(const char *s, int c); +char *strstr(const char *s1, const char *s2); +int memcmp(const void *s1, const void *s2, size_t n); +void *memmove(void *dest, const void *src, size_t n); +void *memchr(const void *s, int c, size_t n); +uint8_t csum8(uint8_t *buf, uint32_t len); + +static inline void *memset(void *s, int c, size_t n) +{ + return __builtin_memset(s, c, n); +} + +static inline void *memcpy(void *dest, const void *src, size_t n) +{ + return __builtin_memcpy(dest, src, n); +} + +void *malloc_align(int n, int align); +void *malloc_fseg_align(int n, int align); + +static inline void *malloc(int n) +{ + return malloc_align(n, 16); +} + +static inline void *malloc_fseg(int n) +{ + return malloc_fseg_align(n, 16); +} +#endif diff --git a/roms/qboot/linuxboot.c b/roms/qboot/linuxboot.c new file mode 100644 index 000000000..251bcb621 --- /dev/null +++ b/roms/qboot/linuxboot.c @@ -0,0 +1,130 @@ +#include "bios.h" +#include "linuxboot.h" +#include "memaccess.h" +#include "ioport.h" +#include "start_info.h" +#include "string.h" +#include "stdio.h" +#include "benchmark.h" + +struct hvm_start_info start_info = {0}; + +bool parse_bzimage(struct linuxboot_args *args) +{ + uint8_t *header = args->header; + + uint32_t real_addr, cmdline_addr, prot_addr, initrd_addr; + uint32_t setup_size; + uint32_t initrd_max; + uint16_t protocol; + + if (ldl_p(header+0x202) == 0x53726448) + protocol = lduw_p(header+0x206); + else { + /* assume multiboot. TODO: scan for header */ + return false; + // protocol = 0; + } + + if (protocol < 0x200 || !(header[0x211] & 0x01)) { + /* Low kernel */ + real_addr = 0x90000; + cmdline_addr = (0x9a000 - args->cmdline_size) & ~15; + prot_addr = 0x10000; + } else if (protocol < 0x202) { + /* High but ancient kernel */ + real_addr = 0x90000; + cmdline_addr = (0x9a000 - args->cmdline_size) & ~15; + prot_addr = 0x100000; + } else { + /* High and recent kernel */ + real_addr = 0x10000; + cmdline_addr = 0x20000; + prot_addr = 0x100000; + } + + if (protocol >= 0x203) + initrd_max = ldl_p(header+0x22c); + else + initrd_max = 0x37ffffff; + if (initrd_max > lowmem - 1) + initrd_max = lowmem - 1; + + if (protocol >= 0x202) + stl_p(header+0x228, cmdline_addr); + else { + stw_p(header+0x20, 0xA33F); + stw_p(header+0x22, cmdline_addr-real_addr); + } + + /* High nybble = B reserved for QEMU; low nybble is revision number. + * If this code is substantially changed, you may want to consider + * incrementing the revision. */ + if (protocol >= 0x200) + header[0x210] = 0xB0; + + /* heap */ + if (protocol >= 0x201) { + header[0x211] |= 0x80; /* CAN_USE_HEAP */ + stw_p(header+0x224, cmdline_addr-real_addr-0x200); + } + + if (args->initrd_size) + initrd_addr = (initrd_max - args->initrd_size) & ~4095; + else + initrd_addr = 0; + stl_p(header+0x218, initrd_addr); + stl_p(header+0x21c, args->initrd_size); + + /* load kernel and setup */ + setup_size = header[0x1f1]; + if (setup_size == 0) + setup_size = 4; + + args->setup_size = (setup_size+1)*512; + args->kernel_size = args->vmlinuz_size - setup_size; + args->initrd_addr = (void *)initrd_addr; + args->setup_addr = (void *)real_addr; + args->kernel_addr = (void *)prot_addr; + args->cmdline_addr = (void *)cmdline_addr; + return true; +} + +void boot_bzimage(struct linuxboot_args *args) +{ + memcpy(args->setup_addr, args->header, sizeof(args->header)); +#ifdef BENCHMARK_HACK + /* Exit just before getting to vmlinuz, so that it is easy + * to time/profile the firmware. + */ + outb(LINUX_EXIT_PORT, LINUX_START_BOOT); +#endif + asm volatile( + "ljmp $0x18, $pm16_boot_linux - 0xf0000" + : : + "b" (((uintptr_t) args->setup_addr) >> 4), + "d" (args->cmdline_addr - args->setup_addr - 16)); + panic(); +} + +/* BX = address of data block + * DX = cmdline_addr-setup_addr-16 + */ +asm("pm16_boot_linux:" + ".code16;" + "mov $0x20, %ax; mov %ax, %ds; mov %ax, %es;" + "mov %ax, %fs; mov %ax, %gs; mov %ax, %ss;" + "xor %eax, %eax; mov %eax, %cr0;" + "ljmpl $0xf000, $(1f - 0xf0000); 1:" + "mov %bx, %ds; mov %bx, %es;" + "mov %bx, %fs; mov %bx, %gs; mov %bx, %ss;" + "mov %dx, %sp;" + "add $0x20, %bx; pushw %bx;" // push CS + "pushw %ax;" // push IP + "xor %ebx, %ebx;" + "xor %ecx, %ecx;" + "xor %edx, %edx;" + "xor %edi, %edi;" + "xor %ebp, %ebp;" + "lret;" + ".code32"); diff --git a/roms/qboot/main.c b/roms/qboot/main.c new file mode 100644 index 000000000..afa2200cb --- /dev/null +++ b/roms/qboot/main.c @@ -0,0 +1,105 @@ +#include <stdbool.h> +#include "bios.h" +#include "stdio.h" +#include "e820.h" +#include "string.h" +#include "segment.h" +#include "fw_cfg.h" +#include "pci.h" +#include "benchmark.h" +#include "smbios.h" + +static void set_realmode_int(int vec, void *p) +{ + uint16_t *realmode_idt = (uint16_t *) 0; + realmode_idt[vec * 2] = flat_to_off16((uintptr_t) p); + realmode_idt[vec * 2 + 1] = flat_to_seg16((uintptr_t) p); +} + +static void setup_idt(void) +{ + int i; + for (i = 0; i < 0x100; i++) + set_realmode_int(i, bios_intfake); + for (i = 8; i < 16; i++) + set_realmode_int(i, bios_irq); + for (i = 0x70; i < 0x78; i++) + set_realmode_int(i, bios_irq); + set_realmode_int(0x10, bios_int10); + set_realmode_int(0x15, bios_int15); +} + +/* Top of memory below 4GB. */ +uint32_t lowmem; +struct e820map *e820; +static bool have_mmconfig; + +static void extract_e820(void) +{ + int id = fw_cfg_file_id("etc/e820"); + uint32_t size; + int nr_map; + int i; + + if (id == -1) + panic(); + + size = fw_cfg_file_size(id); + nr_map = size / sizeof(e820->map[0]) + 5; + + e820 = malloc(offsetof(struct e820map, map[nr_map])); + e820->nr_map = nr_map; + e820->map[0] = (struct e820entry) + { .addr = 0, .size = 639 * 1024, .type = E820_RAM }; /* low RAM */ + e820->map[1] = (struct e820entry) + { .addr = 639 * 1024, .size = 1024, .type = E820_RESERVED }; /* EBDA */ + e820->map[2] = (struct e820entry) + { .addr = 0xd0000, .size = 128 * 1024, .type = E820_NVS }; /* ACPI tables */ + e820->map[3] = (struct e820entry) + { .addr = 0xf0000, .size = 64 * 1024, .type = E820_RESERVED }; /* firmware */ + + i = 4; + if (have_mmconfig) + e820->map[i++] = (struct e820entry) + { .addr = PCIE_MMCONFIG_BASE, .size = PCIE_MMCONFIG_SIZE, .type = E820_RESERVED }; + else + nr_map--; + + fw_cfg_read_file(id, &e820->map[i], size); + for (; i < e820->nr_map; i++) + if (e820->map[i].addr == 0) { + lowmem = e820->map[i].size; + e820->map[i].addr = 1024 * 1024; + e820->map[i].size -= 1024 * 1024; + break; + } + + e820_seg = ((uintptr_t) e820) >> 4; +} + +int __attribute__ ((section (".text.startup"))) main(void) +{ + bool have_pci; +#ifdef BENCHMARK_HACK + outb(FW_EXIT_PORT, FW_START); +#endif + have_pci = setup_hw(); + + // Only the 16-bit trampoline for vmlinuz and the 16-bit interrupt + // handlers need to run from the F-segment, but keep things simple + // and jump there. From this point we can modify global variables. + asm("ljmp $0x8, $1f; 1:"); + + have_mmconfig = setup_mmconfig(); + if (have_pci) { + setup_pci(); + } + setup_idt(); + fw_cfg_setup(); + extract_acpi(); + extract_e820(); + setup_mptable(); + extract_smbios(); + boot_from_fwcfg(); + panic(); +} diff --git a/roms/qboot/malloc.c b/roms/qboot/malloc.c new file mode 100644 index 000000000..bd0ac0f23 --- /dev/null +++ b/roms/qboot/malloc.c @@ -0,0 +1,21 @@ +#include <stdint.h> +#include "string.h" +#include "bios.h" + +static uint8_t *fseg_base = &edata; +static uint8_t *malloc_top = &stext; + +void *malloc_align(int n, int align) +{ + malloc_top = (uint8_t *) ((uintptr_t)(malloc_top - n) & -align); + return malloc_top; +} + +void *malloc_fseg_align(int n, int align) +{ + void *p; + fseg_base = (uint8_t *) (((uintptr_t)fseg_base + align - 1) & -align); + p = fseg_base; + fseg_base += n; + return p; +} diff --git a/roms/qboot/meson.build b/roms/qboot/meson.build new file mode 100644 index 000000000..d060f756e --- /dev/null +++ b/roms/qboot/meson.build @@ -0,0 +1,53 @@ +project('qboot', 'c', meson_version: '>=0.49.0') + +cc = meson.get_compiler('c') +objcopy = find_program('objcopy') + +c_args = [ + '-m32', + '-march=i386', + '-mregparm=3', + '-fno-stack-protector', + '-fno-delete-null-pointer-checks', + '-ffreestanding', + '-mstringop-strategy=rep_byte', + '-minline-all-stringops', + '-fno-pic', +] + +link_args = ['-nostdlib', '-m32'] +link_args += cc.get_supported_link_arguments('-Wl,--build-id=none') +link_args += '-Wl,-T' + meson.current_source_dir() / 'flat.lds' +link_args += cc.get_supported_link_arguments(['-no-pie']) + +elf = executable( + 'bios.bin.elf', + files( + 'code16.c', + 'code32seg.c', + 'cstart.S', + 'entry.S', + 'fw_cfg.c', + 'hwsetup.c', + 'linuxboot.c', + 'main.c', + 'malloc.c', + 'mptable.c', + 'pci.c', + 'printf.c', + 'string.c', + 'smbios.c', + 'tables.c', + ), + c_args: c_args, + include_directories: include_directories('include'), + link_args: link_args, +) + +bin = custom_target( + 'bios.bin', + output: 'bios.bin', + input: elf, + command: [objcopy, '-O', 'binary', '@INPUT@', '@OUTPUT@'], + build_by_default: true, +) diff --git a/roms/qboot/mptable.c b/roms/qboot/mptable.c new file mode 100644 index 000000000..6d62cef72 --- /dev/null +++ b/roms/qboot/mptable.c @@ -0,0 +1,186 @@ +#include "include/string.h" +#include "bios.h" +#include "fw_cfg.h" +#include "include/mpspec_def.h" + +#define MPTABLE_START 0x9fc00 +#define APIC_VERSION 0x14 +#define MPC_SPEC 0x4 + +#define MP_IRQDIR_DEFAULT 0 +#define MP_IRQDIR_HIGH 1 +#define MP_IRQDIR_LOW 3 + +static const char MPC_OEM[] = "QBOOT "; +static const char MPC_PRODUCT_ID[] = "000000000000"; +static const char BUS_TYPE_ISA[] = "ISA "; + +#define IO_APIC_DEFAULT_PHYS_BASE 0xfec00000 +#define APIC_DEFAULT_PHYS_BASE 0xfee00000 +#define APIC_VERSION 0x14 + +static int mptable_checksum(char *buf, int size) +{ + int i; + int sum = 0; + + for (i = 0; i < size; i++) { + sum += buf[i]; + } + + return sum; +} + +static void mptable_get_cpuid(int *signature, int *features) +{ + int ebx, ecx; + + asm("cpuid" + : "=a" (*signature), "=b" (ebx), "=c" (ecx), "=d" (*features) + : "0" (1)); +} + +void setup_mptable(void) +{ + struct mpf_intel *mpf; + struct mpc_table *table; + struct mpc_cpu *cpu; + struct mpc_bus *bus; + struct mpc_ioapic *ioapic; + struct mpc_intsrc *intsrc; + struct mpc_lintsrc *lintsrc; + const char mpc_signature[] = MPC_SIGNATURE; + const char smp_magic_ident[] = "_MP_"; + int cpuid_stepping, cpuid_features; + int irq0_override = 0; + int checksum = 0; + int offset = 0; + int num_cpus; + int ssize; + int i; + + ssize = sizeof(struct mpf_intel); + + mpf = (struct mpf_intel *) MPTABLE_START; + memset(mpf, 0, ssize); + memcpy(mpf->signature, smp_magic_ident, sizeof(smp_magic_ident) - 1); + mpf->length = 1; + mpf->specification = 4; + mpf->physptr = MPTABLE_START + ssize; + mpf->checksum -= mptable_checksum((char *) mpf, ssize); + + offset += ssize; + ssize = sizeof(struct mpc_table); + + table = (struct mpc_table *) (MPTABLE_START + offset); + memset(table, 0, ssize); + memcpy(table->signature, mpc_signature, sizeof(mpc_signature) - 1); + table->spec = MPC_SPEC; + memcpy(table->oem, MPC_OEM, sizeof(MPC_OEM) - 1); + memcpy(table->productid, MPC_PRODUCT_ID, sizeof(MPC_PRODUCT_ID) - 1); + table->lapic = APIC_DEFAULT_PHYS_BASE; + + offset += ssize; + ssize = sizeof(struct mpc_cpu); + + fw_cfg_select(FW_CFG_NB_CPUS); + num_cpus = fw_cfg_readl_le(); + mptable_get_cpuid(&cpuid_stepping, &cpuid_features); + + for (i = 0; i < num_cpus; i++) { + cpu = (struct mpc_cpu *) (MPTABLE_START + offset); + memset(cpu, 0, ssize); + cpu->type = MP_PROCESSOR; + cpu->apicid = i; + cpu->apicver = APIC_VERSION; + cpu->cpuflag = CPU_ENABLED; + if (i == 0) { + cpu->cpuflag |= CPU_BOOTPROCESSOR; + } + cpu->cpufeature = cpuid_stepping; + cpu->featureflag = cpuid_features; + checksum += mptable_checksum((char *) cpu, ssize); + offset += ssize; + } + + ssize = sizeof(struct mpc_bus); + + bus = (struct mpc_bus *) (MPTABLE_START + offset); + memset(bus, 0, ssize); + bus->type = MP_BUS; + bus->busid = 0; + memcpy(bus->bustype, BUS_TYPE_ISA, sizeof(BUS_TYPE_ISA) - 1); + checksum += mptable_checksum((char *) bus, ssize); + + offset += ssize; + ssize = sizeof(struct mpc_ioapic); + + ioapic = (struct mpc_ioapic *) (MPTABLE_START + offset); + memset(ioapic, 0, ssize); + ioapic->type = MP_IOAPIC; + ioapic->apicid = num_cpus + 1; + ioapic->apicver = APIC_VERSION; + ioapic->flags = MPC_APIC_USABLE; + ioapic->apicaddr = IO_APIC_DEFAULT_PHYS_BASE; + checksum += mptable_checksum((char *) ioapic, ssize); + + offset += ssize; + ssize = sizeof(struct mpc_intsrc); + + fw_cfg_select(FW_CFG_IRQ0_OVERRIDE); + irq0_override = fw_cfg_readl_le(); + + for (i = 0; i < 16; i++) { + intsrc = (struct mpc_intsrc *) (MPTABLE_START + offset); + memset(intsrc, 0, ssize); + intsrc->type = MP_INTSRC; + intsrc->irqtype = mp_INT; + intsrc->irqflag = MP_IRQDIR_DEFAULT; + intsrc->srcbus = 0; + intsrc->srcbusirq = i; + intsrc->dstapic = num_cpus + 1; + intsrc->dstirq = i; + if (irq0_override) { + if (i == 0) { + intsrc->dstirq = 2; + } else if (i == 2) { + // Don't update offset nor checksum + continue; + } + } + checksum += mptable_checksum((char *) intsrc, ssize); + offset += ssize; + } + + ssize = sizeof(struct mpc_lintsrc); + + lintsrc = (struct mpc_lintsrc *) (MPTABLE_START + offset); + memset(lintsrc, 0, ssize); + lintsrc->type = MP_LINTSRC; + lintsrc->irqtype = mp_ExtINT; + lintsrc->irqflag = MP_IRQDIR_DEFAULT; + lintsrc->srcbusid = 0; + lintsrc->srcbusirq = 0; + lintsrc->destapic = 0; + lintsrc->destapiclint = 0; + checksum += mptable_checksum((char *) lintsrc, ssize); + + offset += ssize; + + lintsrc = (struct mpc_lintsrc *) (MPTABLE_START + offset); + lintsrc->type = MP_LINTSRC; + lintsrc->irqtype = mp_NMI; + lintsrc->irqflag = MP_IRQDIR_DEFAULT; + lintsrc->srcbusid = 0; + lintsrc->srcbusirq = 0; + lintsrc->destapic = 0xFF; + lintsrc->destapiclint = 1; + checksum += mptable_checksum((char *) lintsrc, ssize); + + offset += ssize; + ssize = sizeof(struct mpc_table); + + table->length = offset - sizeof(struct mpf_intel); + checksum += mptable_checksum((char *) table, ssize); + table->checksum -= checksum; +} diff --git a/roms/qboot/pci.c b/roms/qboot/pci.c new file mode 100644 index 000000000..63ebda6a0 --- /dev/null +++ b/roms/qboot/pci.c @@ -0,0 +1,177 @@ +#include "bios.h" +#include "ioport.h" +#include "pci.h" +#include "string.h" + +static uint16_t addend; +static uint8_t bus, bridge_head; +static bool use_i440fx_routing; +static int bridge_count; +uint8_t max_bus; + +static void do_setup_pci_bus(void); + +static void pci_foreach(void(*fn)(uint32_t bdf, uint32_t id, uint8_t type)) +{ + int d, f; + for (d = 0; d < 32; d++) { + for (f = 0; f < 8; f++) { + uint32_t bdf = (bus * 256) + (d * 8) + f; + uint32_t id = pci_config_readl(bdf, PCI_VENDOR_ID); + uint16_t vendor; + uint8_t type; + + /* 0x0000 or 0xFFFF? Skip. */ + vendor = id & 0xFFFF; + if ((uint16_t)(vendor + 1) <= 1) { + if (f == 0) + break; + else + continue; + } + + type = pci_config_readb(bdf, PCI_HEADER_TYPE); + fn(bdf, id, type); + + if (f == 0 && !(type & PCI_HEADER_TYPE_MULTI_FUNCTION)) + break; + } + } +} + +static void do_setup_pci_irq(uint32_t bdf, int pin) +{ + int dev = (bdf >> 3) & 0x1f; + int lnk, irq; + + irq = pci_config_readb(bdf, PCI_INTERRUPT_LINE); + if (irq != 0) + return; + + lnk = addend + pin; + if (use_i440fx_routing) + lnk += dev - 1; + else { + /* Q35 devices 25-31 all use LNKA. Devices 0-24 have + * a slightly different mapping. + */ + if (dev <= 24) + lnk += dev; + } + lnk &= 3; + + irq = lnk & 2 ? 11 : 10; + pci_config_writeb(bdf, PCI_INTERRUPT_LINE, irq); +} + +static void do_setup_pci(uint32_t bdf, uint32_t id, uint8_t type) +{ + uint16_t class; + uint8_t pin; + + pin = pci_config_readb(bdf, PCI_INTERRUPT_PIN); + if (pin != 0) + do_setup_pci_irq(bdf, pin); + + if (type & PCI_HEADER_TYPE_BRIDGE) { + uint32_t ctl; + + ctl = pci_config_readw(bdf, PCI_BRIDGE_CONTROL); + pci_config_writew(bdf, PCI_BRIDGE_CONTROL, + ctl | PCI_BRIDGE_CTL_SERR); + } + + class = pci_config_readw(bdf, PCI_CLASS_DEVICE); + switch (class) { + case PCI_CLASS_STORAGE_IDE: + pci_config_writel(bdf, 0x10, 0x1f0); + pci_config_writel(bdf, 0x14, 0x3f4); + pci_config_writel(bdf, 0x18, 0x170); + pci_config_writel(bdf, 0x1c, 0x374); + if (id == (PCI_VENDOR_ID_INTEL | (PCI_DEVICE_ID_INTEL_82371SB_1 << 16)) + || id == (PCI_VENDOR_ID_INTEL | (PCI_DEVICE_ID_INTEL_82371AB << 16))) { + /* Enable IDE0 and IDE1. */ + pci_config_writew(bdf, 0x40, 0x8000); + pci_config_writew(bdf, 0x42, 0x8000); + } + break; + + case PCI_CLASS_BRIDGE_PCI: + pci_config_writeb(bdf, PCI_PRIMARY_BUS, bus); + /* prevent accidental access to unintended devices */ + pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, 0); + /* + * Insert at the head of a linked list of bridges. + * do_setup_pci_bus will use it later to initialize secondary + * buses with a recursive call. + */ + pci_config_writeb(bdf, PCI_SECONDARY_BUS, bridge_head); + bridge_head = (uint8_t)(bdf & 0xFF); + bridge_count++; + break; + } +} + +static void do_setup_pci_bus(void) +{ + uint8_t save_bus, next_head; + int i; + + bridge_head = 0xFF; + bridge_count = 0; + + /* Discover all PCI devices and block bridges */ + pci_foreach(do_setup_pci); + + next_head = bridge_head; + save_bus = bus; + + /* Configure bridges on this bus and recursively setup new busses */ + for (i = bridge_count; i > 0; i--) { + uint32_t bdf = (save_bus * 256) + next_head; + + next_head = pci_config_readb(bdf, PCI_SECONDARY_BUS); + + bus = ++max_bus; + pci_config_writeb(bdf, PCI_SECONDARY_BUS, bus); + pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, 255); + + /* Add PCI bridge device id for the recursive call. */ + addend += (bdf >> 3) & 0x1f; + do_setup_pci_bus(); + addend -= (bdf >> 3) & 0x1f; + + pci_config_writeb(bdf, PCI_SUBORDINATE_BUS, max_bus); + } +} + +void setup_bios32(void) +{ + char *bios32 = malloc_fseg_align(16, 16); + void *bios32_entry_ = &bios32_entry; + int i; + + memcpy(bios32, "_32_", 4); + memcpy(bios32 + 4, &bios32_entry_, 4); + bios32[8] = 0; + bios32[9] = 1; + memset(bios32 + 10, 0, 6); + for (i = 0; i <= 9; i++) + bios32[10] -= bios32[i]; +} + +void setup_pci(void) +{ + const int bdf = 0; + + uint32_t id = pci_config_readl(bdf, 0); + if (id == (PCI_VENDOR_ID_INTEL | (PCI_DEVICE_ID_INTEL_82441 << 16))) + use_i440fx_routing = true; + else if (id == (PCI_VENDOR_ID_INTEL | (PCI_DEVICE_ID_INTEL_Q35_MCH << 16))) + use_i440fx_routing = false; + else + panic(); + + do_setup_pci_bus(); + setup_bios32(); +} diff --git a/roms/qboot/printf.c b/roms/qboot/printf.c new file mode 100644 index 000000000..bfbd15c1b --- /dev/null +++ b/roms/qboot/printf.c @@ -0,0 +1,253 @@ +#include "bios.h" +#include "stdio.h" +#include "string.h" +#include "stdlib.h" +#include "ioport.h" + +typedef struct pstream { + char *buffer; + int remain; + int added; +} pstream_t; + +typedef struct strprops { + char pad; + int npad; +} strprops_t; + +static void addchar(pstream_t *p, char c) +{ + if (p->remain) { + *p->buffer++ = c; + --p->remain; + } + ++p->added; +} + +int puts(const char *c) +{ + int n = 0; + while (c[n]) + outb(0x3f8, c[n++]); + return n; +} + +void print_str(pstream_t *p, const char *s, strprops_t props) +{ + const char *s_orig = s; + int npad = props.npad; + + if (npad > 0) { + npad -= strlen(s_orig); + while (npad > 0) { + addchar(p, props.pad); + --npad; + } + } + + while (*s) + addchar(p, *s++); + + if (npad < 0) { + props.pad = ' '; /* ignore '0' flag with '-' flag */ + npad += strlen(s_orig); + while (npad < 0) { + addchar(p, props.pad); + ++npad; + } + } +} + +static char digits[16] = "0123456789abcdef"; + +void print_int(pstream_t *ps, long n, int base, strprops_t props) +{ + char buf[sizeof(long) * 3 + 2], *p = buf; + int s = 0, i; + + if (n < 0) { + n = -n; + s = 1; + } + + while (n) { + *p++ = digits[n % base]; + n /= base; + } + + if (s) + *p++ = '-'; + + if (p == buf) + *p++ = '0'; + + for (i = 0; i < (p - buf) / 2; ++i) { + char tmp; + + tmp = buf[i]; + buf[i] = p[-1-i]; + p[-1-i] = tmp; + } + + *p = 0; + + print_str(ps, buf, props); +} + +void print_unsigned(pstream_t *ps, unsigned long n, int base, + strprops_t props) +{ + char buf[sizeof(long) * 3 + 1], *p = buf; + int i; + + while (n) { + *p++ = digits[n % base]; + n /= base; + } + + if (p == buf) + *p++ = '0'; + + for (i = 0; i < (p - buf) / 2; ++i) { + char tmp; + + tmp = buf[i]; + buf[i] = p[-1-i]; + p[-1-i] = tmp; + } + + *p = 0; + + print_str(ps, buf, props); +} + +static int fmtnum(const char **fmt) +{ + const char *f = *fmt; + int len = 0, num; + + if (*f == '-') + ++f, ++len; + + while (*f >= '0' && *f <= '9') + ++f, ++len; + + num = atol(*fmt); + *fmt += len; + return num; +} + +int vsnprintf(char *buf, int size, const char *fmt, va_list va) +{ + pstream_t s; + + s.buffer = buf; + s.remain = size - 1; + s.added = 0; + while (*fmt) { + char f = *fmt++; + int nlong = 0; + strprops_t props; + memset(&props, 0, sizeof(props)); + props.pad = ' '; + + if (f != '%') { + addchar(&s, f); + continue; + } + morefmt: + f = *fmt++; + if (f == '%') { + addchar(&s, '%'); + continue; + } + if (f == 'c') { + addchar(&s, va_arg(va, int)); + continue; + } + if (f == '\0') { + --fmt; + continue; + } + if (f == '0') { + props.pad = '0'; + ++fmt; + /* fall through */ + } + if ((f >= '1' && f <= '9') || f == '-') { + --fmt; + props.npad = fmtnum(&fmt); + goto morefmt; + } + if (f == 'l') { + ++nlong; + goto morefmt; + } + if (f == 'd') { + switch (nlong) { + case 0: + print_int(&s, va_arg(va, int), 10, props); + break; + case 1: + print_int(&s, va_arg(va, long), 10, props); + break; + default: + panic(); + break; + } + continue; + } + if (f == 'x') { + switch (nlong) { + case 0: + print_unsigned(&s, va_arg(va, unsigned), 16, props); + break; + case 1: + print_unsigned(&s, va_arg(va, unsigned long), 16, props); + break; + default: + panic(); + break; + } + continue; + } + if (f == 'p') { + print_str(&s, "0x", props); + print_unsigned(&s, (unsigned long)va_arg(va, void *), 16, props); + continue; + } + if (f == 's') { + print_str(&s, va_arg(va, const char *), props); + continue; + } + addchar(&s, f); + } + *s.buffer = 0; + ++s.added; + return s.added; +} + + +int snprintf(char *buf, int size, const char *fmt, ...) +{ + va_list va; + int r; + + va_start(va, fmt); + r = vsnprintf(buf, size, fmt, va); + va_end(va); + return r; +} + +int printf(const char *fmt, ...) +{ + va_list va; + char buf[2000]; + int r; + + va_start(va, fmt); + r = vsnprintf(buf, sizeof buf, fmt, va); + va_end(va); + puts(buf); + return r; +} diff --git a/roms/qboot/smbios.c b/roms/qboot/smbios.c new file mode 100644 index 000000000..356501538 --- /dev/null +++ b/roms/qboot/smbios.c @@ -0,0 +1,174 @@ +#include <stdint.h> +#include "smbios.h" +#include "string.h" +#include "fw_cfg.h" + +#define VERSION "0.1" +#define BIOS_NAME "qboot" +#define BIOS_DATE "11/11/2019" + +struct smbios_entry_point { + uint32_t signature; + uint8_t checksum; + uint8_t length; + uint8_t smbios_major_version; + uint8_t smbios_minor_version; + uint16_t max_structure_size; + uint8_t entry_point_revision; + uint8_t formatted_area[5]; + char intermediate_anchor_string[5]; + uint8_t intermediate_checksum; + uint16_t structure_table_length; + uint32_t structure_table_address; + uint16_t number_of_structures; + uint8_t smbios_bcd_revision; +} __attribute__((packed)); + +struct smbios_structure_header { + uint8_t type; + uint8_t length; + uint16_t handle; +} __attribute__((packed)); + +struct smbios_type_0 { + struct smbios_structure_header header; + uint8_t vendor_str; + uint8_t bios_version_str; + uint16_t bios_starting_address_segment; + uint8_t bios_release_date_str; + uint8_t bios_rom_size; + uint8_t bios_characteristics[8]; + uint8_t bios_characteristics_extension_bytes[2]; + uint8_t system_bios_major_release; + uint8_t system_bios_minor_release; + uint8_t embedded_controller_major_release; + uint8_t embedded_controller_minor_release; +} __attribute__((packed)); + +#define set_str_field_or_skip(type, field, value) \ + do { \ + int size = (value != NULL) ? strlen(value) + 1 : 0; \ + if (size > 1) { \ + memcpy(end, value, size); \ + end += size; \ + p->field = ++str_index; \ + } else { \ + p->field = 0; \ + } \ + } while (0) + +static void smbios_new_type_0(void *start, const char *vendor, + const char *version, const char *date) +{ + struct smbios_type_0 *p = (struct smbios_type_0 *)start; + char *end = (char *)start + sizeof(struct smbios_type_0); + int str_index = 0; + + p->header.type = 0; + p->header.length = sizeof(struct smbios_type_0); + p->header.handle = 0; + + set_str_field_or_skip(0, vendor_str, vendor); + set_str_field_or_skip(0, bios_version_str, version); + p->bios_starting_address_segment = 0xe800; + set_str_field_or_skip(0, bios_release_date_str, date); + + p->bios_rom_size = 0; /* FIXME */ + + /* BIOS characteristics not supported */ + memset(p->bios_characteristics, 0, 8); + p->bios_characteristics[0] = 0x08; + + /* Enable targeted content distribution (needed for SVVP) */ + p->bios_characteristics_extension_bytes[0] = 0; + p->bios_characteristics_extension_bytes[1] = 4; + + p->system_bios_major_release = 0; + p->system_bios_minor_release = 0; + p->embedded_controller_major_release = 0xFF; + p->embedded_controller_minor_release = 0xFF; + + *end = 0; + end++; + if (!str_index) { + *end = 0; + end++; + } +} + +static struct smbios_structure_header *smbios_next(struct smbios_entry_point *ep, + struct smbios_structure_header *hdr) +{ + if (!ep) + return NULL; + void *start = (void *)ep->structure_table_address; + void *end = start + ep->structure_table_length; + void *ptr; + + if (hdr == NULL) + ptr = start; + else { + ptr = hdr; + if (ptr + sizeof(*hdr) > end) + return NULL; + ptr += hdr->length + 2; + while (ptr < end && + (*(uint8_t*)(ptr-1) != '\0' || *(uint8_t*)(ptr-2) != '\0')) + ptr++; + } + hdr = ptr; + if (ptr >= end || ptr + sizeof(*hdr) >= end || ptr + hdr->length >= end) + return NULL; + return hdr; +} + +void extract_smbios(void) +{ + int id; + struct smbios_entry_point *ep; + uint8_t *qtables; + uint16_t qtables_length; + struct smbios_structure_header *table_header = NULL; + int need_t0 = 1; + uint16_t t0_len = sizeof(struct smbios_type_0) + strlen(BIOS_NAME) + + strlen(VERSION) + strlen(BIOS_DATE) + 4; + + id = fw_cfg_file_id("etc/smbios/smbios-anchor"); + if (id == -1 || (sizeof(*ep) != fw_cfg_file_size(id))) + return ; + /* malloc_fseg is 16-byte alignment default */ + if (!(ep = malloc_fseg(sizeof(*ep)))) + return; + fw_cfg_read_file(id, ep, sizeof(*ep)); + + qtables_length = ep->structure_table_length; + id = fw_cfg_file_id("etc/smbios/smbios-tables"); + if (id == -1 || qtables_length != fw_cfg_file_size(id)) + return ; + if (!(qtables = malloc_fseg(qtables_length + t0_len))) + return ; + qtables += t0_len; + fw_cfg_read_file(id, qtables, qtables_length); + + ep->structure_table_address = (uint32_t) qtables; + ep->structure_table_length = qtables_length; + + while ((table_header = smbios_next(ep, table_header))) { + if (table_header->type == 0) { + need_t0 = 0; + break; + } + } + + if (need_t0) { + smbios_new_type_0(qtables - t0_len, BIOS_NAME, VERSION, BIOS_DATE); + ep->number_of_structures++; + ep->structure_table_address -= t0_len; + ep->structure_table_length += t0_len; + if (t0_len > ep->max_structure_size) + ep->max_structure_size = t0_len; + } + + ep->checksum -= csum8((void *) ep, 0x10); + ep->intermediate_checksum -= csum8((void *) ep + 0x10, ep->length - 0x10); +} diff --git a/roms/qboot/string.c b/roms/qboot/string.c new file mode 100644 index 000000000..e3ebfa7e4 --- /dev/null +++ b/roms/qboot/string.c @@ -0,0 +1,142 @@ +#include "string.h" + +unsigned long strlen(const char *buf) +{ + unsigned long len = 0; + + while (*buf++) + ++len; + return len; +} + +char *strcat(char *dest, const char *src) +{ + char *p = dest; + + while (*p) + ++p; + while ((*p++ = *src++) != 0) + ; + return dest; +} + +char *strcpy(char *dest, const char *src) +{ + *dest = 0; + return strcat(dest, src); +} + +int strcmp(const char *a, const char *b) +{ + while (*a == *b) { + if (*a == '\0') { + break; + } + ++a, ++b; + } + return *a - *b; +} + +char *strchr(const char *s, int c) +{ + while (*s != (char)c) + if (*s++ == '\0') + return NULL; + return (char *)s; +} + +char *strstr(const char *s1, const char *s2) +{ + size_t l1, l2; + + l2 = strlen(s2); + if (!l2) + return (char *)s1; + l1 = strlen(s1); + while (l1 >= l2) { + l1--; + if (!memcmp(s1, s2, l2)) + return (char *)s1; + s1++; + } + return NULL; +} + +int memcmp(const void *s1, const void *s2, size_t n) +{ + const unsigned char *a = s1, *b = s2; + int ret = 0; + + while (n--) { + ret = *a - *b; + if (ret) + break; + ++a, ++b; + } + return ret; +} + +void *memmove(void *dest, const void *src, size_t n) +{ + const unsigned char *s = src; + unsigned char *d = dest; + + if (d <= s) { + while (n--) + *d++ = *s++; + } else { + d += n, s += n; + while (n--) + *--d = *--s; + } + return dest; +} + +void *memchr(const void *s, int c, size_t n) +{ + const unsigned char *str = s, chr = (unsigned char)c; + + while (n--) + if (*str++ == chr) + return (void *)(str - 1); + return NULL; +} + +long atol(const char *ptr) +{ + long acc = 0; + const char *s = ptr; + int neg, c; + + while (*s == ' ' || *s == '\t') + s++; + if (*s == '-'){ + neg = 1; + s++; + } else { + neg = 0; + if (*s == '+') + s++; + } + + while (*s) { + if (*s < '0' || *s > '9') + break; + c = *s - '0'; + acc = acc * 10 + c; + s++; + } + + if (neg) + acc = -acc; + + return acc; +} + +uint8_t csum8(uint8_t *buf, uint32_t len) +{ + uint32_t s = 0; + while (len-- > 0) + s += *buf++; + return s; +} diff --git a/roms/qboot/tables.c b/roms/qboot/tables.c new file mode 100644 index 000000000..9934a913a --- /dev/null +++ b/roms/qboot/tables.c @@ -0,0 +1,159 @@ +#include "bios.h" +#include "stdio.h" +#include "fw_cfg.h" +#include "string.h" +#include "start_info.h" + +extern struct hvm_start_info start_info; + +struct loader_cmd { + uint32_t cmd; + union { +#define CMD_QUIT 0 +#define CMD_ALLOC 1 + struct { + char file[56]; + uint32_t align; + uint8_t zone; + } alloc; +#define CMD_PTR 2 + struct { + char dest[56]; + char src[56]; + uint32_t offset; + uint8_t size; + } ptr; +#define CMD_CHECKSUM 3 + struct { + char file[56]; + uint32_t offset; + uint32_t start; + uint32_t len; + } checksum; + uint8_t pad[124]; + }; +} __attribute__((__packed__)); + +enum { + ALLOC_HIGH = 1, + ALLOC_FSEG = 2 +}; + +static uint8_t *file_address[20]; + +static inline void *id_to_addr(int fw_cfg_id) +{ + return file_address[fw_cfg_id]; +} + +static inline void set_file_addr(int fw_cfg_id, void *p) +{ + file_address[fw_cfg_id] = p; +} + +static void do_alloc(char *file, uint32_t align, uint8_t zone) +{ + int id = fw_cfg_file_id(file); + int n = fw_cfg_file_size(id); + char *p; + + if (id == -1) + panic(); + + if (align < 16) + align = 16; + + if (zone == ALLOC_FSEG) + p = malloc_fseg_align(n, align); + else + p = malloc_align(n, align); + + set_file_addr(id, p); + fw_cfg_read_file(id, p, n); + + /* For PVH boot, save the PA where the RSDP is stored */ + if (zone == ALLOC_FSEG) { + if (!memcmp(p, "RSD PTR ", 8)) { + start_info.rsdp_paddr = (uintptr_t)id_to_addr(id); + } + } +} + +static void do_ptr(char *dest, char *src, uint32_t offset, uint8_t size) +{ + char *p, *q; + int id; + union { + long long ll; + char b[8]; + } data; + + id = fw_cfg_file_id(src); + p = id_to_addr(id); + if (!p) + panic(); + + id = fw_cfg_file_id(dest); + q = id_to_addr(id); + if (!q) + panic(); + + q += offset; + + /* Assumes little endian */ + data.ll = 0; + memcpy(&data.b, q, size); + data.ll += (uintptr_t) p; + memcpy(q, &data.b, size); +} + +static void do_checksum(char *file, uint32_t offset, uint32_t start, uint32_t len) +{ + uint8_t *p; + int id; + int n; + + id = fw_cfg_file_id(file); + p = id_to_addr(id); + if (!p) + panic(); + + n = fw_cfg_file_size(id); + if (offset >= n || n < start || len > n - start) + panic(); + + p[offset] -= csum8(&p[start], len); +} + +void extract_acpi(void) +{ + int id = fw_cfg_file_id("etc/table-loader"); + int n = fw_cfg_file_size(id); + struct loader_cmd script[n / sizeof(struct loader_cmd)]; + int i; + + if (!n) + return; + + fw_cfg_read_file(id, script, n); + + for (i = 0; i < ARRAY_SIZE(script); i++) { + struct loader_cmd *s = &script[i]; + switch(script[i].cmd) { + case CMD_ALLOC: + do_alloc(s->alloc.file, s->alloc.align, s->alloc.zone); + break; + case CMD_PTR: + do_ptr(s->ptr.dest, s->ptr.src, s->ptr.offset, s->ptr.size); + break; + case CMD_CHECKSUM: + do_checksum(s->checksum.file, s->checksum.offset, + s->checksum.start, s->checksum.len); + break; + case CMD_QUIT: + return; + default: + panic(); + } + } +} |