aboutsummaryrefslogtreecommitdiffstats
path: root/roms/qboot
diff options
context:
space:
mode:
Diffstat (limited to 'roms/qboot')
-rw-r--r--roms/qboot/LICENSE340
-rw-r--r--roms/qboot/README47
-rw-r--r--roms/qboot/benchmark.h14
-rw-r--r--roms/qboot/code16.c122
-rw-r--r--roms/qboot/code32seg.c75
-rw-r--r--roms/qboot/cstart.S45
-rw-r--r--roms/qboot/entry.S138
-rw-r--r--roms/qboot/flat.lds28
-rw-r--r--roms/qboot/fw_cfg.c315
-rw-r--r--roms/qboot/hwsetup.c160
-rw-r--r--roms/qboot/include/assembly.h26
-rw-r--r--roms/qboot/include/bios.h92
-rw-r--r--roms/qboot/include/bswap.h34
-rw-r--r--roms/qboot/include/const.h27
-rw-r--r--roms/qboot/include/e820.h44
-rw-r--r--roms/qboot/include/fw_cfg.h120
-rw-r--r--roms/qboot/include/ioport.h50
-rw-r--r--roms/qboot/include/linuxboot.h19
-rw-r--r--roms/qboot/include/memaccess.h30
-rw-r--r--roms/qboot/include/mpspec_def.h182
-rw-r--r--roms/qboot/include/multiboot.h66
-rw-r--r--roms/qboot/include/pci.h78
-rw-r--r--roms/qboot/include/processor-flags.h153
-rw-r--r--roms/qboot/include/segment.h19
-rw-r--r--roms/qboot/include/smbios.h1
-rw-r--r--roms/qboot/include/start_info.h146
-rw-r--r--roms/qboot/include/stdio.h11
-rw-r--r--roms/qboot/include/stdlib.h6
-rw-r--r--roms/qboot/include/string.h40
-rw-r--r--roms/qboot/linuxboot.c130
-rw-r--r--roms/qboot/main.c105
-rw-r--r--roms/qboot/malloc.c21
-rw-r--r--roms/qboot/meson.build53
-rw-r--r--roms/qboot/mptable.c186
-rw-r--r--roms/qboot/pci.c177
-rw-r--r--roms/qboot/printf.c253
-rw-r--r--roms/qboot/smbios.c174
-rw-r--r--roms/qboot/string.c142
-rw-r--r--roms/qboot/tables.c159
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();
+ }
+ }
+}