efi_io.c revision 76d05dc695b06c4e987bb8078f78032441e1430c
1/* 2 * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of the 7 * License, or any later version. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * 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, write to the Free Software 16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 */ 18 19FILE_LICENCE ( GPL2_OR_LATER ); 20 21#include <assert.h> 22#include <gpxe/io.h> 23#include <gpxe/efi/efi.h> 24#include <gpxe/efi/Protocol/CpuIo.h> 25#include <gpxe/efi/efi_io.h> 26 27/** @file 28 * 29 * gPXE I/O API for EFI 30 * 31 */ 32 33/** CPU I/O protocol */ 34static EFI_CPU_IO_PROTOCOL *cpu_io; 35EFI_REQUIRE_PROTOCOL ( EFI_CPU_IO_PROTOCOL, &cpu_io ); 36 37/** Maximum address that can be used for port I/O */ 38#define MAX_PORT_ADDRESS 0xffff 39 40/** 41 * Determine whether or not address is a port I/O address 42 * 43 * @v io_addr I/O address 44 * @v is_port I/O address is a port I/O address 45 */ 46#define IS_PORT_ADDRESS(io_addr) \ 47 ( ( ( intptr_t ) (io_addr) ) <= MAX_PORT_ADDRESS ) 48 49/** 50 * Determine EFI CPU I/O width code 51 * 52 * @v size Size of value 53 * @ret width EFI width code 54 * 55 * Someone at Intel clearly gets paid by the number of lines of code 56 * they write. No-one should ever be able to make I/O this 57 * convoluted. The EFI_CPU_IO_PROTOCOL_WIDTH enum is my favourite 58 * idiocy. 59 */ 60static EFI_CPU_IO_PROTOCOL_WIDTH efi_width ( size_t size ) { 61 switch ( size ) { 62 case 1 : return EfiCpuIoWidthFifoUint8; 63 case 2 : return EfiCpuIoWidthFifoUint16; 64 case 4 : return EfiCpuIoWidthFifoUint32; 65 case 8 : return EfiCpuIoWidthFifoUint64; 66 default : 67 assert ( 0 ); 68 /* I wonder what this will actually do... */ 69 return EfiCpuIoWidthMaximum; 70 } 71} 72 73/** 74 * Read from device 75 * 76 * @v io_addr I/O address 77 * @v size Size of value 78 * @ret data Value read 79 */ 80unsigned long long efi_ioread ( volatile void *io_addr, size_t size ) { 81 EFI_CPU_IO_PROTOCOL_IO_MEM read; 82 unsigned long long data = 0; 83 EFI_STATUS efirc; 84 85 read = ( IS_PORT_ADDRESS ( io_addr ) ? 86 cpu_io->Io.Read : cpu_io->Mem.Read ); 87 88 if ( ( efirc = read ( cpu_io, efi_width ( size ), 89 ( intptr_t ) io_addr, 1, 90 ( void * ) &data ) ) != 0 ) { 91 DBG ( "EFI I/O read at %p failed: %s\n", 92 io_addr, efi_strerror ( efirc ) ); 93 return -1ULL; 94 } 95 96 return data; 97} 98 99/** 100 * Write to device 101 * 102 * @v data Value to write 103 * @v io_addr I/O address 104 * @v size Size of value 105 */ 106void efi_iowrite ( unsigned long long data, volatile void *io_addr, 107 size_t size ) { 108 EFI_CPU_IO_PROTOCOL_IO_MEM write; 109 EFI_STATUS efirc; 110 111 write = ( IS_PORT_ADDRESS ( io_addr ) ? 112 cpu_io->Io.Write : cpu_io->Mem.Write ); 113 114 if ( ( efirc = write ( cpu_io, efi_width ( size ), 115 ( intptr_t ) io_addr, 1, 116 ( void * ) &data ) ) != 0 ) { 117 DBG ( "EFI I/O write at %p failed: %s\n", 118 io_addr, efi_strerror ( efirc ) ); 119 } 120} 121 122/** 123 * String read from device 124 * 125 * @v io_addr I/O address 126 * @v data Data buffer 127 * @v size Size of values 128 * @v count Number of values to read 129 */ 130void efi_ioreads ( volatile void *io_addr, void *data, 131 size_t size, unsigned int count ) { 132 EFI_CPU_IO_PROTOCOL_IO_MEM read; 133 EFI_STATUS efirc; 134 135 read = ( IS_PORT_ADDRESS ( io_addr ) ? 136 cpu_io->Io.Read : cpu_io->Mem.Read ); 137 138 if ( ( efirc = read ( cpu_io, efi_width ( size ), 139 ( intptr_t ) io_addr, count, 140 ( void * ) data ) ) != 0 ) { 141 DBG ( "EFI I/O string read at %p failed: %s\n", 142 io_addr, efi_strerror ( efirc ) ); 143 } 144} 145 146/** 147 * String write to device 148 * 149 * @v io_addr I/O address 150 * @v data Data buffer 151 * @v size Size of values 152 * @v count Number of values to write 153 */ 154void efi_iowrites ( volatile void *io_addr, const void *data, 155 size_t size, unsigned int count ) { 156 EFI_CPU_IO_PROTOCOL_IO_MEM write; 157 EFI_STATUS efirc; 158 159 write = ( IS_PORT_ADDRESS ( io_addr ) ? 160 cpu_io->Io.Write : cpu_io->Mem.Write ); 161 162 if ( ( efirc = write ( cpu_io, efi_width ( size ), 163 ( intptr_t ) io_addr, count, 164 ( void * ) data ) ) != 0 ) { 165 DBG ( "EFI I/O write at %p failed: %s\n", 166 io_addr, efi_strerror ( efirc ) ); 167 } 168} 169 170/** 171 * Wait for I/O-mapped operation to complete 172 * 173 */ 174static void efi_iodelay ( void ) { 175 /* Write to non-existent port. Probably x86-only. */ 176 outb ( 0, 0x80 ); 177} 178 179PROVIDE_IOAPI_INLINE ( efi, phys_to_bus ); 180PROVIDE_IOAPI_INLINE ( efi, bus_to_phys ); 181PROVIDE_IOAPI_INLINE ( efi, ioremap ); 182PROVIDE_IOAPI_INLINE ( efi, iounmap ); 183PROVIDE_IOAPI_INLINE ( efi, io_to_bus ); 184PROVIDE_IOAPI_INLINE ( efi, readb ); 185PROVIDE_IOAPI_INLINE ( efi, readw ); 186PROVIDE_IOAPI_INLINE ( efi, readl ); 187PROVIDE_IOAPI_INLINE ( efi, readq ); 188PROVIDE_IOAPI_INLINE ( efi, writeb ); 189PROVIDE_IOAPI_INLINE ( efi, writew ); 190PROVIDE_IOAPI_INLINE ( efi, writel ); 191PROVIDE_IOAPI_INLINE ( efi, writeq ); 192PROVIDE_IOAPI_INLINE ( efi, inb ); 193PROVIDE_IOAPI_INLINE ( efi, inw ); 194PROVIDE_IOAPI_INLINE ( efi, inl ); 195PROVIDE_IOAPI_INLINE ( efi, outb ); 196PROVIDE_IOAPI_INLINE ( efi, outw ); 197PROVIDE_IOAPI_INLINE ( efi, outl ); 198PROVIDE_IOAPI_INLINE ( efi, insb ); 199PROVIDE_IOAPI_INLINE ( efi, insw ); 200PROVIDE_IOAPI_INLINE ( efi, insl ); 201PROVIDE_IOAPI_INLINE ( efi, outsb ); 202PROVIDE_IOAPI_INLINE ( efi, outsw ); 203PROVIDE_IOAPI_INLINE ( efi, outsl ); 204PROVIDE_IOAPI ( efi, iodelay, efi_iodelay ); 205PROVIDE_IOAPI_INLINE ( efi, mb ); 206