diff mbox series

[2/4] linuxboot_dma: move common functions in a new header

Message ID 20190111131836.107549-3-sgarzare@redhat.com (mailing list archive)
State New, archived
Headers show
Series pvh: add new PVH option rom | expand

Commit Message

Stefano Garzarella Jan. 11, 2019, 1:18 p.m. UTC
In order to allow other option roms to use these common
useful functions and definitions, this patch put them
in a new C header file called optrom.h, and also add
useful out*() in*() functions for different size.

Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
---
 pc-bios/optionrom/linuxboot_dma.c |  98 ++++-----------------------
 pc-bios/optionrom/optrom.h        | 109 ++++++++++++++++++++++++++++++
 pc-bios/optionrom/optrom_fw_cfg.h |  92 +++++++++++++++++++++++++
 3 files changed, 216 insertions(+), 83 deletions(-)
 create mode 100644 pc-bios/optionrom/optrom.h
 create mode 100644 pc-bios/optionrom/optrom_fw_cfg.h

Comments

Stefan Hajnoczi Jan. 11, 2019, 4:26 p.m. UTC | #1
On Fri, Jan 11, 2019 at 02:18:34PM +0100, Stefano Garzarella wrote:
> In order to allow other option roms to use these common
> useful functions and definitions, this patch put them
> in a new C header file called optrom.h, and also add
> useful out*() in*() functions for different size.

It's usually helpful to modify code in a one patch and move it in a
separate patch.  This patch does both, so it's a harder to review.
Don't worry about changing it now, but something to try in the future.

> +#include <stdint.h>
> +#include "optrom.h"
> +#include "optrom_fw_cfg.h"

Can these be moved to the top of the file like a regular C source file?

> diff --git a/pc-bios/optionrom/optrom.h b/pc-bios/optionrom/optrom.h
> new file mode 100644
> index 0000000000..36f43b43fd
> --- /dev/null
> +++ b/pc-bios/optionrom/optrom.h
> @@ -0,0 +1,109 @@
> +/*
> + * Common Option ROM Functions for C code
> + *
> + * 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, see <http://www.gnu.org/licenses/>.
> + *
> + * Copyright (c) 2015-2019 Red Hat Inc.
> + *   Authors:
> + *     Marc Marí <marc.mari.barcelo@gmail.com>
> + *     Richard W.M. Jones <rjones@redhat.com>
> + *     Stefano Garzarella <sgarzare@redhat.com>
> + */
> +
> +#ifndef OPTROM_H
> +#define OPTROM_H
> +
> +#include "../../include/standard-headers/linux/qemu_fw_cfg.h"

This depends on <stdint.h>, please include it first.
Stefano Garzarella Jan. 11, 2019, 5:42 p.m. UTC | #2
On Fri, Jan 11, 2019 at 5:27 PM Stefan Hajnoczi <stefanha@gmail.com> wrote:
>
> On Fri, Jan 11, 2019 at 02:18:34PM +0100, Stefano Garzarella wrote:
> > In order to allow other option roms to use these common
> > useful functions and definitions, this patch put them
> > in a new C header file called optrom.h, and also add
> > useful out*() in*() functions for different size.
>
> It's usually helpful to modify code in a one patch and move it in a
> separate patch.  This patch does both, so it's a harder to review.
> Don't worry about changing it now, but something to try in the future.

Thanks for the tip!

>
> > +#include <stdint.h>
> > +#include "optrom.h"
> > +#include "optrom_fw_cfg.h"
>
> Can these be moved to the top of the file like a regular C source file?

Yes, I'll move them to the top.

>
> > diff --git a/pc-bios/optionrom/optrom.h b/pc-bios/optionrom/optrom.h
> > new file mode 100644
> > index 0000000000..36f43b43fd
> > --- /dev/null
> > +++ b/pc-bios/optionrom/optrom.h
> > @@ -0,0 +1,109 @@
> > +/*
> > + * Common Option ROM Functions for C code
> > + *
> > + * 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, see <http://www.gnu.org/licenses/>.
> > + *
> > + * Copyright (c) 2015-2019 Red Hat Inc.
> > + *   Authors:
> > + *     Marc Marí <marc.mari.barcelo@gmail.com>
> > + *     Richard W.M. Jones <rjones@redhat.com>
> > + *     Stefano Garzarella <sgarzare@redhat.com>
> > + */
> > +
> > +#ifndef OPTROM_H
> > +#define OPTROM_H
> > +
> > +#include "../../include/standard-headers/linux/qemu_fw_cfg.h"
>
> This depends on <stdint.h>, please include it first.

Sure.


Thanks,
Stefano
Michael S. Tsirkin Jan. 11, 2019, 5:48 p.m. UTC | #3
On Fri, Jan 11, 2019 at 06:42:25PM +0100, Stefano Garzarella wrote:
> On Fri, Jan 11, 2019 at 5:27 PM Stefan Hajnoczi <stefanha@gmail.com> wrote:
> >
> > On Fri, Jan 11, 2019 at 02:18:34PM +0100, Stefano Garzarella wrote:
> > > In order to allow other option roms to use these common
> > > useful functions and definitions, this patch put them
> > > in a new C header file called optrom.h, and also add
> > > useful out*() in*() functions for different size.
> >
> > It's usually helpful to modify code in a one patch and move it in a
> > separate patch.  This patch does both, so it's a harder to review.
> > Don't worry about changing it now, but something to try in the future.
> 
> Thanks for the tip!
> 
> >
> > > +#include <stdint.h>
> > > +#include "optrom.h"
> > > +#include "optrom_fw_cfg.h"
> >
> > Can these be moved to the top of the file like a regular C source file?
> 
> Yes, I'll move them to the top.
> 
> >
> > > diff --git a/pc-bios/optionrom/optrom.h b/pc-bios/optionrom/optrom.h
> > > new file mode 100644
> > > index 0000000000..36f43b43fd
> > > --- /dev/null
> > > +++ b/pc-bios/optionrom/optrom.h
> > > @@ -0,0 +1,109 @@
> > > +/*
> > > + * Common Option ROM Functions for C code
> > > + *
> > > + * 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, see <http://www.gnu.org/licenses/>.
> > > + *
> > > + * Copyright (c) 2015-2019 Red Hat Inc.
> > > + *   Authors:
> > > + *     Marc Marí <marc.mari.barcelo@gmail.com>
> > > + *     Richard W.M. Jones <rjones@redhat.com>
> > > + *     Stefano Garzarella <sgarzare@redhat.com>
> > > + */
> > > +
> > > +#ifndef OPTROM_H
> > > +#define OPTROM_H
> > > +
> > > +#include "../../include/standard-headers/linux/qemu_fw_cfg.h"
> >
> > This depends on <stdint.h>, please include it first.
> 
> Sure.
> 
> 
> Thanks,
> Stefano

Better to just pull in qemu/osdep.h

> 
> -- 
> Stefano Garzarella
> Red Hat
Eric Blake Jan. 11, 2019, 5:55 p.m. UTC | #4
On 1/11/19 11:48 AM, Michael S. Tsirkin wrote:

>>>
>>>> diff --git a/pc-bios/optionrom/optrom.h b/pc-bios/optionrom/optrom.h
>>>> new file mode 100644
>>>> index 0000000000..36f43b43fd
>>>> --- /dev/null
>>>> +++ b/pc-bios/optionrom/optrom.h

>>>> +#include "../../include/standard-headers/linux/qemu_fw_cfg.h"
>>>
>>> This depends on <stdint.h>, please include it first.
>>
>> Sure.
>>
>>
>> Thanks,
>> Stefano
> 
> Better to just pull in qemu/osdep.h

Except that qemu/osdep.h should already have been pulled in by whatever
.c file is including this header. We specifically document that .h files
shouldn't need to include osdep.h (and in turn, anything that osdep.h
already pulls in, like <stdint.h>).
Stefano Garzarella Jan. 12, 2019, 6:18 p.m. UTC | #5
On Fri, Jan 11, 2019 at 6:48 PM Michael S. Tsirkin <mst@redhat.com> wrote:
> > > > +#include "../../include/standard-headers/linux/qemu_fw_cfg.h"
> > >
> > > This depends on <stdint.h>, please include it first.
>
> Better to just pull in qemu/osdep.h

This header is used in the option roms that are bare-metal, so IMHO I
can't include qemu/osdep.h here or in the option roms.

Thanks,
Stefano
Stefano Garzarella Jan. 12, 2019, 6:25 p.m. UTC | #6
On Fri, Jan 11, 2019 at 6:55 PM Eric Blake <eblake@redhat.com> wrote:
>
> On 1/11/19 11:48 AM, Michael S. Tsirkin wrote:
>
> >>>
> >>>> diff --git a/pc-bios/optionrom/optrom.h b/pc-bios/optionrom/optrom.h
> >>>> new file mode 100644
> >>>> index 0000000000..36f43b43fd
> >>>> --- /dev/null
> >>>> +++ b/pc-bios/optionrom/optrom.h
>
> >>>> +#include "../../include/standard-headers/linux/qemu_fw_cfg.h"
> >>>
> >>> This depends on <stdint.h>, please include it first.
> >>
> >> Sure.
> >>
> >>
> >> Thanks,
> >> Stefano
> >
> > Better to just pull in qemu/osdep.h
>
> Except that qemu/osdep.h should already have been pulled in by whatever
> .c file is including this header. We specifically document that .h files
> shouldn't need to include osdep.h (and in turn, anything that osdep.h
> already pulls in, like <stdint.h>).

Since I can't include qemu/osdep.h because this header file is used by
the option roms (bare metal), do you think is better to include
stdint.h in optrom.h or in the .c files that use it?
As Stefan pointed out, I'm including qemu_fw_cfg.h in optrom.h and it
depends on stdint.h. In this case, what is the best approach?

Thanks,
Stefano
Eric Blake Jan. 13, 2019, 2:31 a.m. UTC | #7
On 1/12/19 12:25 PM, Stefano Garzarella wrote:

>>> Better to just pull in qemu/osdep.h
>>
>> Except that qemu/osdep.h should already have been pulled in by whatever
>> .c file is including this header. We specifically document that .h files
>> shouldn't need to include osdep.h (and in turn, anything that osdep.h
>> already pulls in, like <stdint.h>).
> 
> Since I can't include qemu/osdep.h because this header file is used by
> the option roms (bare metal), do you think is better to include
> stdint.h in optrom.h or in the .c files that use it?
> As Stefan pointed out, I'm including qemu_fw_cfg.h in optrom.h and it
> depends on stdint.h. In this case, what is the best approach?

If this is one of the exception files that is used to build files
outside of qemu, then its should probably be mentioned as an exception
in scripts/clean-includes (okay, I see that pc-bios/ is already
exempted) - and thus using osdep.h is not an option, but using
<stdint.h> is, because it is the exception to the rule, in relation to
files used only for qemu.
diff mbox series

Patch

diff --git a/pc-bios/optionrom/linuxboot_dma.c b/pc-bios/optionrom/linuxboot_dma.c
index f728dc839f..effb44d5ee 100644
--- a/pc-bios/optionrom/linuxboot_dma.c
+++ b/pc-bios/optionrom/linuxboot_dma.c
@@ -58,21 +58,9 @@  asm(
 "   jmp load_kernel\n"
 );
 
-#define BIOS_CFG_DMA_ADDR_HIGH 0x514
-#define BIOS_CFG_DMA_ADDR_LOW  0x518
-
-#define uint64_t unsigned long long
-#define uint32_t unsigned int
-#define uint16_t unsigned short
-
-#include "../../include/standard-headers/linux/qemu_fw_cfg.h"
-
-#define barrier() asm("" : : : "memory")
-
-static inline void outl(uint32_t value, uint16_t port)
-{
-    asm("outl %0, %w1" : : "a"(value), "Nd"(port));
-}
+#include <stdint.h>
+#include "optrom.h"
+#include "optrom_fw_cfg.h"
 
 static inline void set_es(void *addr)
 {
@@ -80,12 +68,6 @@  static inline void set_es(void *addr)
     asm("movl %0, %%es" : : "r"(seg));
 }
 
-#ifdef __clang__
-#define ADDR32
-#else
-#define ADDR32 "addr32 "
-#endif
-
 static inline uint16_t readw_es(uint16_t offset)
 {
     uint16_t val;
@@ -108,56 +90,6 @@  static inline void writel_es(uint16_t offset, uint32_t val)
     asm(ADDR32 "movl %0, %%es:(%1)" : : "r"(val), "r"((uint32_t)offset));
 }
 
-static inline uint32_t bswap32(uint32_t x)
-{
-    asm("bswapl %0" : "=r" (x) : "0" (x));
-    return x;
-}
-
-static inline uint64_t bswap64(uint64_t x)
-{
-    asm("bswapl %%eax; bswapl %%edx; xchg %%eax, %%edx" : "=A" (x) : "0" (x));
-    return x;
-}
-
-static inline uint64_t cpu_to_be64(uint64_t x)
-{
-    return bswap64(x);
-}
-
-static inline uint32_t cpu_to_be32(uint32_t x)
-{
-    return bswap32(x);
-}
-
-static inline uint32_t be32_to_cpu(uint32_t x)
-{
-    return bswap32(x);
-}
-
-/* clang is happy to inline this function, and bloats the
- * ROM.
- */
-static __attribute__((__noinline__))
-void bios_cfg_read_entry(void *buf, uint16_t entry, uint32_t len)
-{
-    struct fw_cfg_dma_access access;
-    uint32_t control = (entry << 16) | FW_CFG_DMA_CTL_SELECT
-                        | FW_CFG_DMA_CTL_READ;
-
-    access.address = cpu_to_be64((uint64_t)(uint32_t)buf);
-    access.length = cpu_to_be32(len);
-    access.control = cpu_to_be32(control);
-
-    barrier();
-
-    outl(cpu_to_be32((uint32_t)&access), BIOS_CFG_DMA_ADDR_LOW);
-
-    while (be32_to_cpu(access.control) & ~FW_CFG_DMA_CTL_ERROR) {
-        barrier();
-    }
-}
-
 /* Return top of memory using BIOS function E801. */
 static uint32_t get_e801_addr(void)
 {
@@ -211,9 +143,9 @@  void load_kernel(void)
     uint32_t initrd_end_page, max_allowed_page;
     uint32_t segment_addr, stack_addr;
 
-    bios_cfg_read_entry(&setup_addr, FW_CFG_SETUP_ADDR, 4);
-    bios_cfg_read_entry(&setup_size, FW_CFG_SETUP_SIZE, 4);
-    bios_cfg_read_entry(setup_addr, FW_CFG_SETUP_DATA, setup_size);
+    bios_cfg_read_entry_dma(&setup_addr, FW_CFG_SETUP_ADDR, 4);
+    bios_cfg_read_entry_dma(&setup_size, FW_CFG_SETUP_SIZE, 4);
+    bios_cfg_read_entry_dma(setup_addr, FW_CFG_SETUP_DATA, setup_size);
 
     set_es(setup_addr);
 
@@ -223,8 +155,8 @@  void load_kernel(void)
         writel_es(0x22c, 0x37ffffff);
     }
 
-    bios_cfg_read_entry(&initrd_addr, FW_CFG_INITRD_ADDR, 4);
-    bios_cfg_read_entry(&initrd_size, FW_CFG_INITRD_SIZE, 4);
+    bios_cfg_read_entry_dma(&initrd_addr, FW_CFG_INITRD_ADDR, 4);
+    bios_cfg_read_entry_dma(&initrd_size, FW_CFG_INITRD_SIZE, 4);
 
     initrd_end_page = ((uint32_t)(initrd_addr + initrd_size) & -4096);
     max_allowed_page = (readl_es(0x22c) & -4096);
@@ -239,15 +171,15 @@  void load_kernel(void)
 
     }
 
-    bios_cfg_read_entry(initrd_addr, FW_CFG_INITRD_DATA, initrd_size);
+    bios_cfg_read_entry_dma(initrd_addr, FW_CFG_INITRD_DATA, initrd_size);
 
-    bios_cfg_read_entry(&kernel_addr, FW_CFG_KERNEL_ADDR, 4);
-    bios_cfg_read_entry(&kernel_size, FW_CFG_KERNEL_SIZE, 4);
-    bios_cfg_read_entry(kernel_addr, FW_CFG_KERNEL_DATA, kernel_size);
+    bios_cfg_read_entry_dma(&kernel_addr, FW_CFG_KERNEL_ADDR, 4);
+    bios_cfg_read_entry_dma(&kernel_size, FW_CFG_KERNEL_SIZE, 4);
+    bios_cfg_read_entry_dma(kernel_addr, FW_CFG_KERNEL_DATA, kernel_size);
 
-    bios_cfg_read_entry(&cmdline_addr, FW_CFG_CMDLINE_ADDR, 4);
-    bios_cfg_read_entry(&cmdline_size, FW_CFG_CMDLINE_SIZE, 4);
-    bios_cfg_read_entry(cmdline_addr, FW_CFG_CMDLINE_DATA, cmdline_size);
+    bios_cfg_read_entry_dma(&cmdline_addr, FW_CFG_CMDLINE_ADDR, 4);
+    bios_cfg_read_entry_dma(&cmdline_size, FW_CFG_CMDLINE_SIZE, 4);
+    bios_cfg_read_entry_dma(cmdline_addr, FW_CFG_CMDLINE_DATA, cmdline_size);
 
     /* Boot linux */
     segment_addr = ((uint32_t)setup_addr >> 4);
diff --git a/pc-bios/optionrom/optrom.h b/pc-bios/optionrom/optrom.h
new file mode 100644
index 0000000000..36f43b43fd
--- /dev/null
+++ b/pc-bios/optionrom/optrom.h
@@ -0,0 +1,109 @@ 
+/*
+ * Common Option ROM Functions for C code
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (c) 2015-2019 Red Hat Inc.
+ *   Authors:
+ *     Marc Marí <marc.mari.barcelo@gmail.com>
+ *     Richard W.M. Jones <rjones@redhat.com>
+ *     Stefano Garzarella <sgarzare@redhat.com>
+ */
+
+#ifndef OPTROM_H
+#define OPTROM_H
+
+#include "../../include/standard-headers/linux/qemu_fw_cfg.h"
+
+#define barrier() asm("" : : : "memory")
+
+#ifdef __clang__
+#define ADDR32
+#else
+#define ADDR32 "addr32 "
+#endif
+
+static inline void outb(uint8_t value, uint16_t port)
+{
+    asm volatile("outb %0, %w1" : : "a"(value), "Nd"(port));
+}
+
+static inline void outw(uint16_t value, uint16_t port)
+{
+    asm volatile("outw %0, %w1" : : "a"(value), "Nd"(port));
+}
+
+static inline void outl(uint32_t value, uint16_t port)
+{
+    asm volatile("outl %0, %w1" : : "a"(value), "Nd"(port));
+}
+
+static inline uint8_t inb(uint16_t port)
+{
+    uint8_t value;
+
+    asm volatile("outl %w1, %0" : : "a"(value), "Nd"(port));
+    return value;
+}
+
+static inline uint16_t inw(uint16_t port)
+{
+    uint16_t value;
+
+    asm volatile("outl %w1, %0" : : "a"(value), "Nd"(port));
+    return value;
+}
+
+static inline uint32_t inl(uint16_t port)
+{
+    uint32_t value;
+
+    asm volatile("outl %w1, %0" : : "a"(value), "Nd"(port));
+    return value;
+}
+
+static inline void insb(uint16_t port, uint8_t *buf, uint32_t len)
+{
+    asm volatile("rep insb (%%dx), %%es:(%%edi)"
+                 : "+c"(len), "+D"(buf) : "d"(port) : "memory");
+}
+
+static inline uint32_t bswap32(uint32_t x)
+{
+    asm("bswapl %0" : "=r" (x) : "0" (x));
+    return x;
+}
+
+static inline uint64_t bswap64(uint64_t x)
+{
+    asm("bswapl %%eax; bswapl %%edx; xchg %%eax, %%edx" : "=A" (x) : "0" (x));
+    return x;
+}
+
+static inline uint64_t cpu_to_be64(uint64_t x)
+{
+    return bswap64(x);
+}
+
+static inline uint32_t cpu_to_be32(uint32_t x)
+{
+    return bswap32(x);
+}
+
+static inline uint32_t be32_to_cpu(uint32_t x)
+{
+    return bswap32(x);
+}
+
+#endif /* OPTROM_H */
diff --git a/pc-bios/optionrom/optrom_fw_cfg.h b/pc-bios/optionrom/optrom_fw_cfg.h
new file mode 100644
index 0000000000..a3660a5200
--- /dev/null
+++ b/pc-bios/optionrom/optrom_fw_cfg.h
@@ -0,0 +1,92 @@ 
+/*
+ * Common Option ROM Functions for fw_cfg
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (c) 2015-2019 Red Hat Inc.
+ *   Authors:
+ *     Marc Marí <marc.mari.barcelo@gmail.com>
+ *     Richard W.M. Jones <rjones@redhat.com>
+ *     Stefano Garzarella <sgarzare@redhat.com>
+ */
+
+#ifndef OPTROM_FW_CFG_H
+#define OPTROM_FW_CFG_H
+
+#include "../../include/standard-headers/linux/qemu_fw_cfg.h"
+
+#define BIOS_CFG_IOPORT_CFG     0x510
+#define BIOS_CFG_IOPORT_DATA    0x511
+#define BIOS_CFG_DMA_ADDR_HIGH  0x514
+#define BIOS_CFG_DMA_ADDR_LOW   0x518
+
+static __attribute__((unused))
+void bios_cfg_select(uint16_t key)
+{
+    outw(key, BIOS_CFG_IOPORT_CFG);
+}
+
+static __attribute__((unused))
+void bios_cfg_read_entry_io(void *buf, uint16_t entry, uint32_t len)
+{
+    bios_cfg_select(entry);
+    insb(BIOS_CFG_IOPORT_DATA, buf, len);
+}
+
+/*
+ * clang is happy to inline this function, and bloats the
+ * ROM.
+ */
+static __attribute__((__noinline__)) __attribute__((unused))
+void bios_cfg_read_entry_dma(void *buf, uint16_t entry, uint32_t len)
+{
+    struct fw_cfg_dma_access access;
+    uint32_t control = (entry << 16) | FW_CFG_DMA_CTL_SELECT
+                        | FW_CFG_DMA_CTL_READ;
+
+    access.address = cpu_to_be64((uint64_t)(uint32_t)buf);
+    access.length = cpu_to_be32(len);
+    access.control = cpu_to_be32(control);
+
+    barrier();
+
+    outl(cpu_to_be32((uint32_t)&access), BIOS_CFG_DMA_ADDR_LOW);
+
+    while (be32_to_cpu(access.control) & ~FW_CFG_DMA_CTL_ERROR) {
+        barrier();
+    }
+}
+
+static __attribute__((unused))
+void bios_cfg_read_entry(void *buf, uint16_t entry, uint32_t len,
+                         uint32_t version)
+{
+    if (version & FW_CFG_VERSION_DMA) {
+        bios_cfg_read_entry_dma(buf, entry, len);
+    } else {
+        bios_cfg_read_entry_io(buf, entry, len);
+    }
+}
+
+static __attribute__((unused))
+uint32_t bios_cfg_version(void)
+{
+    uint32_t version;
+
+    bios_cfg_read_entry_io(&version, FW_CFG_ID, sizeof(version));
+
+    return version;
+}
+
+#endif /* OPTROM_FW_CFG_H */