On x86, load initrd using DMA interface.
This makes it infinitely faster.
Also, I changed pc-bios/linuxboot.bin from a checked in binary to a
symlink, since that seems to be what the build uses. (Otherwise, it'll
build a linuxboot.bin in the optionrom subdirectory, but helpfully not
use it.) Possibly there's a better way to do this.
Change-Id: Ic3373c2b03e40b9cf461efc848bca8c39c7a312d
diff --git a/pc-bios/linuxboot.bin b/pc-bios/linuxboot.bin
deleted file mode 100644
index 923d179..0000000
--- a/pc-bios/linuxboot.bin
+++ /dev/null
Binary files differ
diff --git a/pc-bios/linuxboot.bin b/pc-bios/linuxboot.bin
new file mode 120000
index 0000000..2293e55
--- /dev/null
+++ b/pc-bios/linuxboot.bin
@@ -0,0 +1 @@
+optionrom/linuxboot.bin
\ No newline at end of file
diff --git a/pc-bios/optionrom/linuxboot.S b/pc-bios/optionrom/linuxboot.S
index ba821ab..fb09f79 100644
--- a/pc-bios/optionrom/linuxboot.S
+++ b/pc-bios/optionrom/linuxboot.S
@@ -165,7 +165,57 @@
/* We're now running in 16-bit CS, but 32-bit ES! */
/* Load kernel and initrd */
- read_fw_blob_addr32_edi(FW_CFG_INITRD)
+
+ /* Load initrd using the "DMA" interface.
+ TODO(vtl): Should really detect whether DMA is supported. */
+ /* Get stack space for a FWCfgDmaAccess struct:
+ typedef struct FWCfgDmaAccess {
+ uint32_t control;
+ uint32_t length;
+ uint64_t address;
+ } FWCfgDmaAccess;
+ */
+ mov %esp, %ebp
+ sub $16, %esp
+ /* Set dma.control (big-endian):
+ high (16-bit) word is FW_CFG_INITRD_DATA
+ low (16-bit) word is FW_CFG_DMA_CTL_READ (0x02) |
+ FW_CFG_DMA_CTL_SELECT (0x08)
+ */
+ movl $(FW_CFG_INITRD_DATA << 16 | 0x02 | 0x08), %eax
+ bswap %eax
+ mov %eax, -16(%bp)
+ /* Get the initrd's length and set dma.length. */
+ read_fw(FW_CFG_INITRD_SIZE)
+ bswap %eax
+ movl %eax, -12(%bp)
+ /* Set dma.address (the address is already in EDI). */
+ movl $0, -8(%bp)
+ mov %edi, %eax
+ bswap %eax
+ mov %eax, -4(%bp)
+ /* Kick off the DMA by outputting the physical address of dma to 0x514
+ (as two 32-bit values). */
+ /* The top value is always 0. */
+ xor %eax, %eax
+ mov $0x514, %dx
+ out %eax, (%dx)
+ /* Calculate the physical address of ESP, and send it to 0x514+4. */
+ mov %ss, %eax
+ movzwl %ax, %eax
+ shl $4, %eax
+ addl %esp, %eax
+ bswap %eax
+ add $4, %dx
+ out %eax, (%dx)
+
+ mov %ebp, %esp
+
+ /* The code for reading the initrd using an I/O port was just:
+ read_fw_blob_addr32_edi(FW_CFG_INITRD)
+ We could also use DMA for the kernel, but we assume it's relatively
+ small.
+ */
read_fw_blob_addr32(FW_CFG_KERNEL)
read_fw_blob_addr32(FW_CFG_CMDLINE)