@@ -55,6 +55,13 @@ struct FWCfgState {
uint32_t cur_offset;
};
+/* Target address and size for DMA operations. This is only used
+ * during boot and across 32 and 64 bit architectures, so only writes
+ * to lower 4GB addresses are supported.
+ */
+static uint32_t dma_addr = 0;
+static uint32_t dma_size = 0;
+
static void fw_cfg_write(FWCfgState *s, uint8_t value)
{
int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
@@ -98,7 +105,16 @@ static uint8_t fw_cfg_read(FWCfgState *s)
if (s->cur_entry == FW_CFG_INVALID || !e->data || s->cur_offset >= e->len)
ret = 0;
- else
+ else if (s->cur_entry & FW_CFG_DMA) {
+ if (dma_size > e->len - s->cur_offset)
+ dma_size = e->len - s->cur_offset;
+
+ cpu_physical_memory_write ((target_phys_addr_t) dma_addr,
+ &e->data[s->cur_offset],
+ dma_size);
+ s->cur_offset += e->len;
+ ret = 1;
+ } else
ret = e->data[s->cur_offset++];
FW_CFG_DPRINTF("read %d\n", ret);
@@ -351,6 +367,10 @@ FWCfgState *fw_cfg_init(uint32_t ctl_port, uint32_t data_port,
fw_cfg_add_i16(s, FW_CFG_NB_CPUS, (uint16_t)smp_cpus);
fw_cfg_add_i16(s, FW_CFG_MAX_CPUS, (uint16_t)max_cpus);
fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu);
+ fw_cfg_add_bytes(s, FW_CFG_DMA_ADDR | FW_CFG_WRITE_CHANNEL,
+ (uint8_t *)&dma_addr, sizeof dma_addr);
+ fw_cfg_add_bytes(s, FW_CFG_DMA_SIZE | FW_CFG_WRITE_CHANNEL,
+ (uint8_t *)&dma_size, sizeof dma_size);
return s;
}
@@ -30,11 +30,15 @@
#define FW_CFG_FILE_FIRST 0x20
#define FW_CFG_FILE_SLOTS 0x10
-#define FW_CFG_MAX_ENTRY (FW_CFG_FILE_FIRST+FW_CFG_FILE_SLOTS)
+#define FW_CFG_DMA_ADDR 0x30
+#define FW_CFG_DMA_SIZE 0x31
+#define FW_CFG_MAX_ENTRY (FW_CFG_DMA_SIZE+1)
+
+#define FW_CFG_DMA 0x2000
#define FW_CFG_WRITE_CHANNEL 0x4000
#define FW_CFG_ARCH_LOCAL 0x8000
-#define FW_CFG_ENTRY_MASK ~(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)
+#define FW_CFG_ENTRY_MASK ~(FW_CFG_DMA | FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL)
#define FW_CFG_INVALID 0xffff
@@ -106,10 +106,10 @@ copy_kernel:
/* We're now running in 16-bit CS, but 32-bit ES! */
/* Load kernel and initrd */
- read_fw_blob_addr32(FW_CFG_KERNEL)
- read_fw_blob_addr32(FW_CFG_INITRD)
- read_fw_blob_addr32(FW_CFG_CMDLINE)
- read_fw_blob_addr32(FW_CFG_SETUP)
+ read_fw_blob_dma(FW_CFG_KERNEL)
+ read_fw_blob_dma(FW_CFG_INITRD)
+ read_fw_blob_dma(FW_CFG_CMDLINE)
+ read_fw_blob_dma(FW_CFG_SETUP)
/* And now jump into Linux! */
mov $0, %eax
@@ -50,6 +50,27 @@
bswap %eax
.endm
+/*
+ * Write %eax to a variable in the fw_cfg device.
+ * In: %eax
+ * Clobbers: %edx
+ */
+.macro write_fw VAR
+ push %eax
+ mov $(\VAR|FW_CFG_WRITE_CHANNEL), %ax
+ mov $BIOS_CFG_IOPORT_CFG, %dx
+ outw %ax, (%dx)
+ pop %eax
+ mov $BIOS_CFG_IOPORT_DATA, %dx
+ outb %al, (%dx)
+ shr $8, %eax
+ outb %al, (%dx)
+ shr $8, %eax
+ outb %al, (%dx)
+ shr $8, %eax
+ outb %al, (%dx)
+.endm
+
#define read_fw_blob_pre(var) \
read_fw var ## _ADDR; \
mov %eax, %edi; \
@@ -87,6 +108,22 @@
*/ \
.dc.b 0x67,0xf3,0x6c
+/*
+ * Fast DMA of data from fw_cfg device into physical memory.
+ * This should be a straight replacement for read_fw_blob and
+ * read_fw_blob_addr32.
+ */
+#define read_fw_blob_dma(var) \
+ read_fw var ## _ADDR; \
+ write_fw FW_CFG_DMA_ADDR; \
+ read_fw var ## _SIZE; \
+ write_fw FW_CFG_DMA_SIZE; \
+ mov $(var ## _DATA|FW_CFG_DMA), %ax; \
+ mov $BIOS_CFG_IOPORT_CFG, %edx; \
+ outw %ax, (%dx); \
+ mov $BIOS_CFG_IOPORT_DATA, %dx; \
+ inb (%dx), %al
+
#define OPTION_ROM_START \
.code16; \
.text; \