qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

linuxboot_dma: move common functions in a new header

In order to allow other option roms to use these common
useful functions and definitions, this patch put them
in two new C header files called optrom.h and
optrom_fw_cfg.h. We also add useful out*() in*()
functions for different size, and new fw_cfg functions
to use when DMA feature is not available.

Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Liam Merwick <liam.merwick@oracle.com>

authored by

Stefano Garzarella and committed by
Paolo Bonzini
6dfa0143 526d7984

+221 -83
+19 -83
pc-bios/optionrom/linuxboot_dma.c
··· 58 58 " jmp load_kernel\n" 59 59 ); 60 60 61 - #define BIOS_CFG_DMA_ADDR_HIGH 0x514 62 - #define BIOS_CFG_DMA_ADDR_LOW 0x518 63 - 64 - #define uint64_t unsigned long long 65 - #define uint32_t unsigned int 66 - #define uint16_t unsigned short 67 - 68 - #include "../../include/standard-headers/linux/qemu_fw_cfg.h" 69 - 70 - #define barrier() asm("" : : : "memory") 71 - 72 - static inline void outl(uint32_t value, uint16_t port) 73 - { 74 - asm("outl %0, %w1" : : "a"(value), "Nd"(port)); 75 - } 61 + /* 62 + * The includes of C headers must be after the asm block to avoid compiler 63 + * errors. 64 + */ 65 + #include <stdint.h> 66 + #include "optrom.h" 67 + #include "optrom_fw_cfg.h" 76 68 77 69 static inline void set_es(void *addr) 78 70 { ··· 80 72 asm("movl %0, %%es" : : "r"(seg)); 81 73 } 82 74 83 - #ifdef __clang__ 84 - #define ADDR32 85 - #else 86 - #define ADDR32 "addr32 " 87 - #endif 88 - 89 75 static inline uint16_t readw_es(uint16_t offset) 90 76 { 91 77 uint16_t val; ··· 108 94 asm(ADDR32 "movl %0, %%es:(%1)" : : "r"(val), "r"((uint32_t)offset)); 109 95 } 110 96 111 - static inline uint32_t bswap32(uint32_t x) 112 - { 113 - asm("bswapl %0" : "=r" (x) : "0" (x)); 114 - return x; 115 - } 116 - 117 - static inline uint64_t bswap64(uint64_t x) 118 - { 119 - asm("bswapl %%eax; bswapl %%edx; xchg %%eax, %%edx" : "=A" (x) : "0" (x)); 120 - return x; 121 - } 122 - 123 - static inline uint64_t cpu_to_be64(uint64_t x) 124 - { 125 - return bswap64(x); 126 - } 127 - 128 - static inline uint32_t cpu_to_be32(uint32_t x) 129 - { 130 - return bswap32(x); 131 - } 132 - 133 - static inline uint32_t be32_to_cpu(uint32_t x) 134 - { 135 - return bswap32(x); 136 - } 137 - 138 - /* clang is happy to inline this function, and bloats the 139 - * ROM. 140 - */ 141 - static __attribute__((__noinline__)) 142 - void bios_cfg_read_entry(void *buf, uint16_t entry, uint32_t len) 143 - { 144 - struct fw_cfg_dma_access access; 145 - uint32_t control = (entry << 16) | FW_CFG_DMA_CTL_SELECT 146 - | FW_CFG_DMA_CTL_READ; 147 - 148 - access.address = cpu_to_be64((uint64_t)(uint32_t)buf); 149 - access.length = cpu_to_be32(len); 150 - access.control = cpu_to_be32(control); 151 - 152 - barrier(); 153 - 154 - outl(cpu_to_be32((uint32_t)&access), BIOS_CFG_DMA_ADDR_LOW); 155 - 156 - while (be32_to_cpu(access.control) & ~FW_CFG_DMA_CTL_ERROR) { 157 - barrier(); 158 - } 159 - } 160 - 161 97 /* Return top of memory using BIOS function E801. */ 162 98 static uint32_t get_e801_addr(void) 163 99 { ··· 211 147 uint32_t initrd_end_page, max_allowed_page; 212 148 uint32_t segment_addr, stack_addr; 213 149 214 - bios_cfg_read_entry(&setup_addr, FW_CFG_SETUP_ADDR, 4); 215 - bios_cfg_read_entry(&setup_size, FW_CFG_SETUP_SIZE, 4); 216 - bios_cfg_read_entry(setup_addr, FW_CFG_SETUP_DATA, setup_size); 150 + bios_cfg_read_entry_dma(&setup_addr, FW_CFG_SETUP_ADDR, 4); 151 + bios_cfg_read_entry_dma(&setup_size, FW_CFG_SETUP_SIZE, 4); 152 + bios_cfg_read_entry_dma(setup_addr, FW_CFG_SETUP_DATA, setup_size); 217 153 218 154 set_es(setup_addr); 219 155 ··· 223 159 writel_es(0x22c, 0x37ffffff); 224 160 } 225 161 226 - bios_cfg_read_entry(&initrd_addr, FW_CFG_INITRD_ADDR, 4); 227 - bios_cfg_read_entry(&initrd_size, FW_CFG_INITRD_SIZE, 4); 162 + bios_cfg_read_entry_dma(&initrd_addr, FW_CFG_INITRD_ADDR, 4); 163 + bios_cfg_read_entry_dma(&initrd_size, FW_CFG_INITRD_SIZE, 4); 228 164 229 165 initrd_end_page = ((uint32_t)(initrd_addr + initrd_size) & -4096); 230 166 max_allowed_page = (readl_es(0x22c) & -4096); ··· 239 175 240 176 } 241 177 242 - bios_cfg_read_entry(initrd_addr, FW_CFG_INITRD_DATA, initrd_size); 178 + bios_cfg_read_entry_dma(initrd_addr, FW_CFG_INITRD_DATA, initrd_size); 243 179 244 - bios_cfg_read_entry(&kernel_addr, FW_CFG_KERNEL_ADDR, 4); 245 - bios_cfg_read_entry(&kernel_size, FW_CFG_KERNEL_SIZE, 4); 246 - bios_cfg_read_entry(kernel_addr, FW_CFG_KERNEL_DATA, kernel_size); 180 + bios_cfg_read_entry_dma(&kernel_addr, FW_CFG_KERNEL_ADDR, 4); 181 + bios_cfg_read_entry_dma(&kernel_size, FW_CFG_KERNEL_SIZE, 4); 182 + bios_cfg_read_entry_dma(kernel_addr, FW_CFG_KERNEL_DATA, kernel_size); 247 183 248 - bios_cfg_read_entry(&cmdline_addr, FW_CFG_CMDLINE_ADDR, 4); 249 - bios_cfg_read_entry(&cmdline_size, FW_CFG_CMDLINE_SIZE, 4); 250 - bios_cfg_read_entry(cmdline_addr, FW_CFG_CMDLINE_DATA, cmdline_size); 184 + bios_cfg_read_entry_dma(&cmdline_addr, FW_CFG_CMDLINE_ADDR, 4); 185 + bios_cfg_read_entry_dma(&cmdline_size, FW_CFG_CMDLINE_SIZE, 4); 186 + bios_cfg_read_entry_dma(cmdline_addr, FW_CFG_CMDLINE_DATA, cmdline_size); 251 187 252 188 /* Boot linux */ 253 189 segment_addr = ((uint32_t)setup_addr >> 4);
+110
pc-bios/optionrom/optrom.h
··· 1 + /* 2 + * Common Option ROM Functions for C code 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation; either version 2 of the License, or 7 + * (at your option) any later version. 8 + * 9 + * This program is distributed in the hope that it will be useful, 10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + * GNU General Public License for more details. 13 + * 14 + * You should have received a copy of the GNU General Public License 15 + * along with this program; if not, see <http://www.gnu.org/licenses/>. 16 + * 17 + * Copyright (c) 2015-2019 Red Hat Inc. 18 + * Authors: 19 + * Marc Marí <marc.mari.barcelo@gmail.com> 20 + * Richard W.M. Jones <rjones@redhat.com> 21 + * Stefano Garzarella <sgarzare@redhat.com> 22 + */ 23 + 24 + #ifndef OPTROM_H 25 + #define OPTROM_H 26 + 27 + #include <stdint.h> 28 + #include "../../include/standard-headers/linux/qemu_fw_cfg.h" 29 + 30 + #define barrier() asm("" : : : "memory") 31 + 32 + #ifdef __clang__ 33 + #define ADDR32 34 + #else 35 + #define ADDR32 "addr32 " 36 + #endif 37 + 38 + static inline void outb(uint8_t value, uint16_t port) 39 + { 40 + asm volatile("outb %0, %w1" : : "a"(value), "Nd"(port)); 41 + } 42 + 43 + static inline void outw(uint16_t value, uint16_t port) 44 + { 45 + asm volatile("outw %0, %w1" : : "a"(value), "Nd"(port)); 46 + } 47 + 48 + static inline void outl(uint32_t value, uint16_t port) 49 + { 50 + asm volatile("outl %0, %w1" : : "a"(value), "Nd"(port)); 51 + } 52 + 53 + static inline uint8_t inb(uint16_t port) 54 + { 55 + uint8_t value; 56 + 57 + asm volatile("inb %w1, %0" : "=a"(value) : "Nd"(port)); 58 + return value; 59 + } 60 + 61 + static inline uint16_t inw(uint16_t port) 62 + { 63 + uint16_t value; 64 + 65 + asm volatile("inw %w1, %0" : "=a"(value) : "Nd"(port)); 66 + return value; 67 + } 68 + 69 + static inline uint32_t inl(uint16_t port) 70 + { 71 + uint32_t value; 72 + 73 + asm volatile("inl %w1, %0" : "=a"(value) : "Nd"(port)); 74 + return value; 75 + } 76 + 77 + static inline void insb(uint16_t port, uint8_t *buf, uint32_t len) 78 + { 79 + asm volatile("rep insb %%dx, %%es:(%%edi)" 80 + : "+c"(len), "+D"(buf) : "d"(port) : "memory"); 81 + } 82 + 83 + static inline uint32_t bswap32(uint32_t x) 84 + { 85 + asm("bswapl %0" : "=r" (x) : "0" (x)); 86 + return x; 87 + } 88 + 89 + static inline uint64_t bswap64(uint64_t x) 90 + { 91 + asm("bswapl %%eax; bswapl %%edx; xchg %%eax, %%edx" : "=A" (x) : "0" (x)); 92 + return x; 93 + } 94 + 95 + static inline uint64_t cpu_to_be64(uint64_t x) 96 + { 97 + return bswap64(x); 98 + } 99 + 100 + static inline uint32_t cpu_to_be32(uint32_t x) 101 + { 102 + return bswap32(x); 103 + } 104 + 105 + static inline uint32_t be32_to_cpu(uint32_t x) 106 + { 107 + return bswap32(x); 108 + } 109 + 110 + #endif /* OPTROM_H */
+92
pc-bios/optionrom/optrom_fw_cfg.h
··· 1 + /* 2 + * Common Option ROM Functions for fw_cfg 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License as published by 6 + * the Free Software Foundation; either version 2 of the License, or 7 + * (at your option) any later version. 8 + * 9 + * This program is distributed in the hope that it will be useful, 10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + * GNU General Public License for more details. 13 + * 14 + * You should have received a copy of the GNU General Public License 15 + * along with this program; if not, see <http://www.gnu.org/licenses/>. 16 + * 17 + * Copyright (c) 2015-2019 Red Hat Inc. 18 + * Authors: 19 + * Marc Marí <marc.mari.barcelo@gmail.com> 20 + * Richard W.M. Jones <rjones@redhat.com> 21 + * Stefano Garzarella <sgarzare@redhat.com> 22 + */ 23 + 24 + #ifndef OPTROM_FW_CFG_H 25 + #define OPTROM_FW_CFG_H 26 + 27 + #include "../../include/standard-headers/linux/qemu_fw_cfg.h" 28 + 29 + #define BIOS_CFG_IOPORT_CFG 0x510 30 + #define BIOS_CFG_IOPORT_DATA 0x511 31 + #define BIOS_CFG_DMA_ADDR_HIGH 0x514 32 + #define BIOS_CFG_DMA_ADDR_LOW 0x518 33 + 34 + static __attribute__((unused)) 35 + void bios_cfg_select(uint16_t key) 36 + { 37 + outw(key, BIOS_CFG_IOPORT_CFG); 38 + } 39 + 40 + static __attribute__((unused)) 41 + void bios_cfg_read_entry_io(void *buf, uint16_t entry, uint32_t len) 42 + { 43 + bios_cfg_select(entry); 44 + insb(BIOS_CFG_IOPORT_DATA, buf, len); 45 + } 46 + 47 + /* 48 + * clang is happy to inline this function, and bloats the 49 + * ROM. 50 + */ 51 + static __attribute__((__noinline__)) __attribute__((unused)) 52 + void bios_cfg_read_entry_dma(void *buf, uint16_t entry, uint32_t len) 53 + { 54 + struct fw_cfg_dma_access access; 55 + uint32_t control = (entry << 16) | FW_CFG_DMA_CTL_SELECT 56 + | FW_CFG_DMA_CTL_READ; 57 + 58 + access.address = cpu_to_be64((uint64_t)(uint32_t)buf); 59 + access.length = cpu_to_be32(len); 60 + access.control = cpu_to_be32(control); 61 + 62 + barrier(); 63 + 64 + outl(cpu_to_be32((uint32_t)&access), BIOS_CFG_DMA_ADDR_LOW); 65 + 66 + while (be32_to_cpu(access.control) & ~FW_CFG_DMA_CTL_ERROR) { 67 + barrier(); 68 + } 69 + } 70 + 71 + static __attribute__((unused)) 72 + void bios_cfg_read_entry(void *buf, uint16_t entry, uint32_t len, 73 + uint32_t version) 74 + { 75 + if (version & FW_CFG_VERSION_DMA) { 76 + bios_cfg_read_entry_dma(buf, entry, len); 77 + } else { 78 + bios_cfg_read_entry_io(buf, entry, len); 79 + } 80 + } 81 + 82 + static __attribute__((unused)) 83 + uint32_t bios_cfg_version(void) 84 + { 85 + uint32_t version; 86 + 87 + bios_cfg_read_entry_io(&version, FW_CFG_ID, sizeof(version)); 88 + 89 + return version; 90 + } 91 + 92 + #endif /* OPTROM_FW_CFG_H */