/* * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>. * * 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 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, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ FILE_LICENCE ( GPL2_OR_LATER ); #include <assert.h> #include <gpxe/io.h> #include <gpxe/efi/efi.h> #include <gpxe/efi/Protocol/CpuIo.h> #include <gpxe/efi/efi_io.h> /** @file * * gPXE I/O API for EFI * */ /** CPU I/O protocol */ static EFI_CPU_IO_PROTOCOL *cpu_io; EFI_REQUIRE_PROTOCOL ( EFI_CPU_IO_PROTOCOL, &cpu_io ); /** Maximum address that can be used for port I/O */ #define MAX_PORT_ADDRESS 0xffff /** * Determine whether or not address is a port I/O address * * @v io_addr I/O address * @v is_port I/O address is a port I/O address */ #define IS_PORT_ADDRESS(io_addr) \ ( ( ( intptr_t ) (io_addr) ) <= MAX_PORT_ADDRESS ) /** * Determine EFI CPU I/O width code * * @v size Size of value * @ret width EFI width code * * Someone at Intel clearly gets paid by the number of lines of code * they write. No-one should ever be able to make I/O this * convoluted. The EFI_CPU_IO_PROTOCOL_WIDTH enum is my favourite * idiocy. */ static EFI_CPU_IO_PROTOCOL_WIDTH efi_width ( size_t size ) { switch ( size ) { case 1 : return EfiCpuIoWidthFifoUint8; case 2 : return EfiCpuIoWidthFifoUint16; case 4 : return EfiCpuIoWidthFifoUint32; case 8 : return EfiCpuIoWidthFifoUint64; default : assert ( 0 ); /* I wonder what this will actually do... */ return EfiCpuIoWidthMaximum; } } /** * Read from device * * @v io_addr I/O address * @v size Size of value * @ret data Value read */ unsigned long long efi_ioread ( volatile void *io_addr, size_t size ) { EFI_CPU_IO_PROTOCOL_IO_MEM read; unsigned long long data = 0; EFI_STATUS efirc; read = ( IS_PORT_ADDRESS ( io_addr ) ? cpu_io->Io.Read : cpu_io->Mem.Read ); if ( ( efirc = read ( cpu_io, efi_width ( size ), ( intptr_t ) io_addr, 1, ( void * ) &data ) ) != 0 ) { DBG ( "EFI I/O read at %p failed: %s\n", io_addr, efi_strerror ( efirc ) ); return -1ULL; } return data; } /** * Write to device * * @v data Value to write * @v io_addr I/O address * @v size Size of value */ void efi_iowrite ( unsigned long long data, volatile void *io_addr, size_t size ) { EFI_CPU_IO_PROTOCOL_IO_MEM write; EFI_STATUS efirc; write = ( IS_PORT_ADDRESS ( io_addr ) ? cpu_io->Io.Write : cpu_io->Mem.Write ); if ( ( efirc = write ( cpu_io, efi_width ( size ), ( intptr_t ) io_addr, 1, ( void * ) &data ) ) != 0 ) { DBG ( "EFI I/O write at %p failed: %s\n", io_addr, efi_strerror ( efirc ) ); } } /** * String read from device * * @v io_addr I/O address * @v data Data buffer * @v size Size of values * @v count Number of values to read */ void efi_ioreads ( volatile void *io_addr, void *data, size_t size, unsigned int count ) { EFI_CPU_IO_PROTOCOL_IO_MEM read; EFI_STATUS efirc; read = ( IS_PORT_ADDRESS ( io_addr ) ? cpu_io->Io.Read : cpu_io->Mem.Read ); if ( ( efirc = read ( cpu_io, efi_width ( size ), ( intptr_t ) io_addr, count, ( void * ) data ) ) != 0 ) { DBG ( "EFI I/O string read at %p failed: %s\n", io_addr, efi_strerror ( efirc ) ); } } /** * String write to device * * @v io_addr I/O address * @v data Data buffer * @v size Size of values * @v count Number of values to write */ void efi_iowrites ( volatile void *io_addr, const void *data, size_t size, unsigned int count ) { EFI_CPU_IO_PROTOCOL_IO_MEM write; EFI_STATUS efirc; write = ( IS_PORT_ADDRESS ( io_addr ) ? cpu_io->Io.Write : cpu_io->Mem.Write ); if ( ( efirc = write ( cpu_io, efi_width ( size ), ( intptr_t ) io_addr, count, ( void * ) data ) ) != 0 ) { DBG ( "EFI I/O write at %p failed: %s\n", io_addr, efi_strerror ( efirc ) ); } } /** * Wait for I/O-mapped operation to complete * */ static void efi_iodelay ( void ) { /* Write to non-existent port. Probably x86-only. */ outb ( 0, 0x80 ); } PROVIDE_IOAPI_INLINE ( efi, phys_to_bus ); PROVIDE_IOAPI_INLINE ( efi, bus_to_phys ); PROVIDE_IOAPI_INLINE ( efi, ioremap ); PROVIDE_IOAPI_INLINE ( efi, iounmap ); PROVIDE_IOAPI_INLINE ( efi, io_to_bus ); PROVIDE_IOAPI_INLINE ( efi, readb ); PROVIDE_IOAPI_INLINE ( efi, readw ); PROVIDE_IOAPI_INLINE ( efi, readl ); PROVIDE_IOAPI_INLINE ( efi, readq ); PROVIDE_IOAPI_INLINE ( efi, writeb ); PROVIDE_IOAPI_INLINE ( efi, writew ); PROVIDE_IOAPI_INLINE ( efi, writel ); PROVIDE_IOAPI_INLINE ( efi, writeq ); PROVIDE_IOAPI_INLINE ( efi, inb ); PROVIDE_IOAPI_INLINE ( efi, inw ); PROVIDE_IOAPI_INLINE ( efi, inl ); PROVIDE_IOAPI_INLINE ( efi, outb ); PROVIDE_IOAPI_INLINE ( efi, outw ); PROVIDE_IOAPI_INLINE ( efi, outl ); PROVIDE_IOAPI_INLINE ( efi, insb ); PROVIDE_IOAPI_INLINE ( efi, insw ); PROVIDE_IOAPI_INLINE ( efi, insl ); PROVIDE_IOAPI_INLINE ( efi, outsb ); PROVIDE_IOAPI_INLINE ( efi, outsw ); PROVIDE_IOAPI_INLINE ( efi, outsl ); PROVIDE_IOAPI ( efi, iodelay, efi_iodelay ); PROVIDE_IOAPI_INLINE ( efi, mb );