diff options
Diffstat (limited to 'src/interface/linux')
| -rw-r--r-- | src/interface/linux/linux_acpi.c | 148 | ||||
| -rw-r--r-- | src/interface/linux/linux_api.c | 551 | ||||
| -rw-r--r-- | src/interface/linux/linux_console.c | 2 | ||||
| -rw-r--r-- | src/interface/linux/linux_entropy.c | 2 | ||||
| -rw-r--r-- | src/interface/linux/linux_nap.c | 2 | ||||
| -rw-r--r-- | src/interface/linux/linux_pci.c | 2 | ||||
| -rw-r--r-- | src/interface/linux/linux_smbios.c | 142 | ||||
| -rw-r--r-- | src/interface/linux/linux_sysfs.c | 96 | ||||
| -rw-r--r-- | src/interface/linux/linux_time.c | 2 | ||||
| -rw-r--r-- | src/interface/linux/linux_timer.c | 2 | ||||
| -rw-r--r-- | src/interface/linux/linux_umalloc.c | 2 | ||||
| -rw-r--r-- | src/interface/linux/linuxprefix.c | 38 |
12 files changed, 917 insertions, 72 deletions
diff --git a/src/interface/linux/linux_acpi.c b/src/interface/linux/linux_acpi.c new file mode 100644 index 000000000..e658936f2 --- /dev/null +++ b/src/interface/linux/linux_acpi.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2021 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <ipxe/linux_api.h> +#include <ipxe/linux_sysfs.h> +#include <ipxe/linux.h> +#include <ipxe/list.h> +#include <ipxe/init.h> +#include <ipxe/umalloc.h> +#include <ipxe/acpi.h> + +/** ACPI sysfs directory */ +#define ACPI_SYSFS_PREFIX "/sys/firmware/acpi/tables/" + +/** A cached ACPI table */ +struct linux_acpi_table { + /** List of cached tables */ + struct list_head list; + /** Signature */ + uint32_t signature; + /** Index */ + unsigned int index; + /** Cached data */ + userptr_t data; +}; + +/** List of cached ACPI tables */ +static LIST_HEAD ( linux_acpi_tables ); + +/** + * Locate ACPI table + * + * @v signature Requested table signature + * @v index Requested index of table with this signature + * @ret table Table, or UNULL if not found + */ +static userptr_t linux_acpi_find ( uint32_t signature, unsigned int index ) { + struct linux_acpi_table *table; + struct acpi_header *header; + union { + uint32_t signature; + char filename[5]; + } u; + static const char prefix[] = ACPI_SYSFS_PREFIX; + char filename[ sizeof ( prefix ) - 1 /* NUL */ + 4 /* signature */ + + 3 /* "999" */ + 1 /* NUL */ ]; + int len; + int rc; + + /* Check for existing table */ + list_for_each_entry ( table, &linux_acpi_tables, list ) { + if ( ( table->signature == signature ) && + ( table->index == index ) ) + return table->data; + } + + /* Allocate a new table */ + table = malloc ( sizeof ( *table ) ); + if ( ! table ) + goto err_alloc; + table->signature = signature; + table->index = index; + + /* Construct filename (including numeric suffix) */ + memset ( &u, 0, sizeof ( u ) ); + u.signature = le32_to_cpu ( signature ); + snprintf ( filename, sizeof ( filename ), "%s%s%d", prefix, + u.filename, ( index + 1 ) ); + + /* Read file (with or without numeric suffix for index 0) */ + len = linux_sysfs_read ( filename, &table->data ); + if ( ( len < 0 ) && ( index == 0 ) ) { + filename[ sizeof ( prefix ) - 1 /* NUL */ + + 4 /* signature */ ] = '\0'; + len = linux_sysfs_read ( filename, &table->data ); + } + if ( len < 0 ) { + rc = len; + DBGC ( &linux_acpi_tables, "ACPI could not read %s: %s\n", + filename, strerror ( rc ) ); + goto err_read; + } + header = user_to_virt ( table->data, 0 ); + if ( ( ( ( size_t ) len ) < sizeof ( *header ) ) || + ( ( ( size_t ) len ) < le32_to_cpu ( header->length ) ) ) { + rc = -ENOENT; + DBGC ( &linux_acpi_tables, "ACPI underlength %s (%d bytes)\n", + filename, len ); + goto err_len; + } + + /* Add to list of tables */ + list_add ( &table->list, &linux_acpi_tables ); + DBGC ( &linux_acpi_tables, "ACPI cached %s\n", filename ); + + return table->data; + + err_len: + ufree ( table->data ); + err_read: + free ( table ); + err_alloc: + return UNULL; +} + +/** + * Free cached ACPI data + * + */ +static void linux_acpi_shutdown ( int booting __unused ) { + struct linux_acpi_table *table; + struct linux_acpi_table *tmp; + + list_for_each_entry_safe ( table, tmp, &linux_acpi_tables, list ) { + list_del ( &table->list ); + ufree ( table->data ); + free ( table ); + } +} + +/** ACPI shutdown function */ +struct startup_fn linux_acpi_startup_fn __startup_fn ( STARTUP_NORMAL ) = { + .name = "linux_acpi", + .shutdown = linux_acpi_shutdown, +}; + +PROVIDE_ACPI ( linux, acpi_find, linux_acpi_find ); diff --git a/src/interface/linux/linux_api.c b/src/interface/linux/linux_api.c new file mode 100644 index 000000000..d1f969aa7 --- /dev/null +++ b/src/interface/linux/linux_api.c @@ -0,0 +1,551 @@ +/* + * Copyright (C) 2010 Piotr JaroszyĆski <p.jaroszynski@gmail.com>. + * Copyright (C) 2021 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#define _GNU_SOURCE +#include <stdint.h> +#include <stdarg.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <fcntl.h> +#include <time.h> +#include <poll.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <ipxe/linux_api.h> +#include <ipxe/slirp.h> + +#ifdef HAVE_LIBSLIRP +#include <slirp/libslirp.h> +#endif + +#undef static_assert +#define static_assert(x) _Static_assert(x, #x) + +/** @file + * + * Linux host API + * + */ + +/** Construct prefixed symbol name */ +#define _C1( x, y ) x ## y +#define _C2( x, y ) _C1 ( x, y ) + +/** Construct prefixed symbol name for iPXE symbols */ +#define IPXE_SYM( symbol ) _C2 ( SYMBOL_PREFIX, symbol ) + +/** Provide a prefixed symbol alias visible to iPXE code */ +#define PROVIDE_IPXE_SYM( symbol ) \ + extern typeof ( symbol ) IPXE_SYM ( symbol ) \ + __attribute__ (( alias ( #symbol) )) + +/** Most recent system call error */ +int linux_errno __attribute__ (( nocommon )); + +/****************************************************************************** + * + * Host entry point + * + ****************************************************************************** + */ + +extern int IPXE_SYM ( _linux_start ) ( int argc, char **argv ); + +/** + * Main entry point + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Exit status + */ +int main ( int argc, char **argv ) { + + return IPXE_SYM ( _linux_start ) ( argc, argv ); +} + +/****************************************************************************** + * + * System call wrappers + * + ****************************************************************************** + */ + +/** + * Wrap open() + * + */ +int __asmcall linux_open ( const char *pathname, int flags, ... ) { + va_list args; + mode_t mode; + int ret; + + va_start ( args, flags ); + mode = va_arg ( args, mode_t ); + va_end ( args ); + ret = open ( pathname, flags, mode ); + if ( ret == -1 ) + linux_errno = errno; + return ret; +} + +/** + * Wrap close() + * + */ +int __asmcall linux_close ( int fd ) { + int ret; + + ret = close ( fd ); + if ( ret == -1 ) + linux_errno = errno; + return ret; +} + +/** + * Wrap lseek() + * + */ +off_t __asmcall linux_lseek ( int fd, off_t offset, int whence ) { + off_t ret; + + ret = lseek ( fd, offset, whence ); + if ( ret == -1 ) + linux_errno = errno; + return ret; +} + +/** + * Wrap read() + * + */ +ssize_t __asmcall linux_read ( int fd, void *buf, size_t count ) { + ssize_t ret; + + ret = read ( fd, buf, count ); + if ( ret == -1 ) + linux_errno = errno; + return ret; +} + +/** + * Wrap write() + * + */ +ssize_t __asmcall linux_write ( int fd, const void *buf, size_t count ) { + ssize_t ret; + + ret = write ( fd, buf, count ); + if ( ret == -1 ) + linux_errno = errno; + return ret; +} + +/** + * Wrap fcntl() + * + */ +int __asmcall linux_fcntl ( int fd, int cmd, ... ) { + va_list args; + long arg; + int ret; + + va_start ( args, cmd ); + arg = va_arg ( args, long ); + va_end ( args ); + ret = fcntl ( fd, cmd, arg ); + if ( ret == -1 ) + linux_errno = errno; + return ret; +} + +/** + * Wrap ioctl() + * + */ +int __asmcall linux_ioctl ( int fd, unsigned long request, ... ) { + va_list args; + void *arg; + int ret; + + va_start ( args, request ); + arg = va_arg ( args, void * ); + va_end ( args ); + ret = ioctl ( fd, request, arg ); + if ( ret == -1 ) + linux_errno = errno; + return ret; +} + +/** + * Wrap part of fstat() + * + */ +int __asmcall linux_fstat_size ( int fd, size_t *size ) { + struct stat stat; + int ret; + + ret = fstat ( fd, &stat ); + *size = stat.st_size; + if ( ret == -1 ) + linux_errno = errno; + return ret; +} + +/** + * Wrap poll() + * + */ +int __asmcall linux_poll ( struct pollfd *fds, unsigned int nfds, + int timeout ) { + int ret; + + ret = poll ( fds, nfds, timeout ); + if ( ret == -1 ) + linux_errno = errno; +} + +/** + * Wrap nanosleep() + * + */ +int __asmcall linux_nanosleep ( const struct timespec *req, + struct timespec *rem ) { + int ret; + + ret = nanosleep ( req, rem ); + if ( ret == -1 ) + linux_errno = errno; + return ret; +} + +/** + * Wrap usleep() + * + */ +int __asmcall linux_usleep ( unsigned int usec ) { + int ret; + + ret = usleep ( usec ); + if ( ret == -1 ) + linux_errno = errno; + return ret; +} + +/** + * Wrap gettimeofday() + * + */ +int __asmcall linux_gettimeofday ( struct timeval *tv, struct timezone *tz ) { + int ret; + + ret = gettimeofday ( tv, tz ); + if ( ret == -1 ) + linux_errno = errno; + return ret; +} + +/** + * Wrap mmap() + * +*/ +void * __asmcall linux_mmap ( void *addr, size_t length, int prot, int flags, + int fd, off_t offset ) { + void *ret; + + ret = mmap ( addr, length, prot, flags, fd, offset ); + if ( ret == MAP_FAILED ) + linux_errno = errno; + return ret; +} + +/** + * Wrap mremap() + * + */ +void * __asmcall linux_mremap ( void *old_address, size_t old_size, + size_t new_size, int flags, ... ) { + va_list args; + void *new_address; + void *ret; + + va_start ( args, flags ); + new_address = va_arg ( args, void * ); + va_end ( args ); + ret = mremap ( old_address, old_size, new_size, flags, new_address ); + if ( ret == MAP_FAILED ) + linux_errno = errno; + return ret; +} + +/** + * Wrap munmap() + * + */ +int __asmcall linux_munmap ( void *addr, size_t length ) { + int ret; + + ret = munmap ( addr, length ); + if ( ret == -1 ) + linux_errno = errno; + return ret; +} + +/** + * Wrap socket() + * + */ +int __asmcall linux_socket ( int domain, int type, int protocol ) { + int ret; + + ret = socket ( domain, type, protocol ); + if ( ret == -1 ) + linux_errno = errno; + return ret; +} + +/** + * Wrap bind() + * + */ +int __asmcall linux_bind ( int sockfd, const struct sockaddr *addr, + size_t addrlen ) { + int ret; + + ret = bind ( sockfd, addr, addrlen ); + if ( ret == -1 ) + linux_errno = errno; + return ret; +} + +/** + * Wrap sendto() + * + */ +ssize_t __asmcall linux_sendto ( int sockfd, const void *buf, size_t len, + int flags, const struct sockaddr *dest_addr, + size_t addrlen ) { + ssize_t ret; + + ret = sendto ( sockfd, buf, len, flags, dest_addr, addrlen ); + if ( ret == -1 ) + linux_errno = errno; + return ret; +} + +/****************************************************************************** + * + * C library wrappers + * + ****************************************************************************** + */ + +/** + * Wrap strerror() + * + */ +const char * __asmcall linux_strerror ( int linux_errno ) { + + return strerror ( linux_errno ); +} + +/****************************************************************************** + * + * libslirp wrappers + * + ****************************************************************************** + */ + +#ifdef HAVE_LIBSLIRP + +/** + * Wrap slirp_new() + * + */ +struct Slirp * __asmcall +linux_slirp_new ( const struct slirp_config *config, + const struct slirp_callbacks *callbacks, void *opaque ) { + const union { + struct slirp_callbacks callbacks; + SlirpCb cb; + } *u = ( ( typeof ( u ) ) callbacks ); + SlirpConfig cfg; + Slirp *slirp; + + /* Translate configuration */ + memset ( &cfg, 0, sizeof ( cfg ) ); + cfg.version = config->version; + cfg.restricted = config->restricted; + cfg.in_enabled = config->in_enabled; + cfg.vnetwork = config->vnetwork; + cfg.vnetmask = config->vnetmask; + cfg.vhost = config->vhost; + cfg.in6_enabled = config->in6_enabled; + memcpy ( &cfg.vprefix_addr6, &config->vprefix_addr6, + sizeof ( cfg.vprefix_addr6 ) ); + cfg.vprefix_len = config->vprefix_len; + memcpy ( &cfg.vhost6, &config->vhost6, sizeof ( cfg.vhost6 ) ); + cfg.vhostname = config->vhostname; + cfg.tftp_server_name = config->tftp_server_name; + cfg.tftp_path = config->tftp_path; + cfg.bootfile = config->bootfile; + cfg.vdhcp_start = config->vdhcp_start; + cfg.vnameserver = config->vnameserver; + memcpy ( &cfg.vnameserver6, &config->vnameserver6, + sizeof ( cfg.vnameserver6 ) ); + cfg.vdnssearch = config->vdnssearch; + cfg.vdomainname = config->vdomainname; + cfg.if_mtu = config->if_mtu; + cfg.if_mru = config->if_mru; + cfg.disable_host_loopback = config->disable_host_loopback; + cfg.enable_emu = config->enable_emu; + + /* Validate callback structure */ + static_assert ( &u->cb.send_packet == &u->callbacks.send_packet ); + static_assert ( &u->cb.guest_error == &u->callbacks.guest_error ); + static_assert ( &u->cb.clock_get_ns == &u->callbacks.clock_get_ns ); + static_assert ( &u->cb.timer_new == &u->callbacks.timer_new ); + static_assert ( &u->cb.timer_free == &u->callbacks.timer_free ); + static_assert ( &u->cb.timer_mod == &u->callbacks.timer_mod ); + static_assert ( &u->cb.register_poll_fd == + &u->callbacks.register_poll_fd ); + static_assert ( &u->cb.unregister_poll_fd == + &u->callbacks.unregister_poll_fd ); + static_assert ( &u->cb.notify == &u->callbacks.notify ); + + /* Create device */ + slirp = slirp_new ( &cfg, &u->cb, opaque ); + + return slirp; +} + +/** + * Wrap slirp_cleanup() + * + */ +void __asmcall linux_slirp_cleanup ( struct Slirp *slirp ) { + + slirp_cleanup ( slirp ); +} + +/** + * Wrap slirp_input() + * + */ +void __asmcall linux_slirp_input ( struct Slirp *slirp, const uint8_t *pkt, + int pkt_len ) { + + slirp_input ( slirp, pkt, pkt_len ); +} + +/** + * Wrap slirp_pollfds_fill() + * + */ +void __asmcall +linux_slirp_pollfds_fill ( struct Slirp *slirp, uint32_t *timeout, + int ( __asmcall * add_poll ) ( int fd, int events, + void *opaque ), + void *opaque ) { + + slirp_pollfds_fill ( slirp, timeout, add_poll, opaque ); +} + +/** + * Wrap slirp_pollfds_poll() + * + */ +void __asmcall +linux_slirp_pollfds_poll ( struct Slirp *slirp, int select_error, + int ( __asmcall * get_revents ) ( int idx, + void *opaque ), + void *opaque ) { + + slirp_pollfds_poll ( slirp, select_error, get_revents, opaque ); +} + +#else /* HAVE_LIBSLIRP */ + +struct Slirp * __asmcall +linux_slirp_new ( const struct slirp_config *config, + const struct slirp_callbacks *callbacks, void *opaque ) { + return NULL; +} + +void __asmcall linux_slirp_cleanup ( struct Slirp *slirp ) { +} + +void __asmcall linux_slirp_input ( struct Slirp *slirp, const uint8_t *pkt, + int pkt_len ) { +} + +void __asmcall +linux_slirp_pollfds_fill ( struct Slirp *slirp, uint32_t *timeout, + int ( __asmcall * add_poll ) ( int fd, int events, + void *opaque ), + void *opaque ) { +} + +void __asmcall +linux_slirp_pollfds_poll ( struct Slirp *slirp, int select_error, + int ( __asmcall * get_revents ) ( int idx, + void *opaque ), + void *opaque ) { +} + +#endif /* HAVE_LIBSLIRP */ + +/****************************************************************************** + * + * Symbol aliases + * + ****************************************************************************** + */ + +PROVIDE_IPXE_SYM ( linux_errno ); +PROVIDE_IPXE_SYM ( linux_open ); +PROVIDE_IPXE_SYM ( linux_close ); +PROVIDE_IPXE_SYM ( linux_lseek ); +PROVIDE_IPXE_SYM ( linux_read ); +PROVIDE_IPXE_SYM ( linux_write ); +PROVIDE_IPXE_SYM ( linux_fcntl ); +PROVIDE_IPXE_SYM ( linux_ioctl ); +PROVIDE_IPXE_SYM ( linux_fstat_size ); +PROVIDE_IPXE_SYM ( linux_poll ); +PROVIDE_IPXE_SYM ( linux_nanosleep ); +PROVIDE_IPXE_SYM ( linux_usleep ); +PROVIDE_IPXE_SYM ( linux_gettimeofday ); +PROVIDE_IPXE_SYM ( linux_mmap ); +PROVIDE_IPXE_SYM ( linux_mremap ); +PROVIDE_IPXE_SYM ( linux_munmap ); +PROVIDE_IPXE_SYM ( linux_socket ); +PROVIDE_IPXE_SYM ( linux_bind ); +PROVIDE_IPXE_SYM ( linux_sendto ); +PROVIDE_IPXE_SYM ( linux_strerror ); +PROVIDE_IPXE_SYM ( linux_slirp_new ); +PROVIDE_IPXE_SYM ( linux_slirp_cleanup ); +PROVIDE_IPXE_SYM ( linux_slirp_input ); +PROVIDE_IPXE_SYM ( linux_slirp_pollfds_fill ); +PROVIDE_IPXE_SYM ( linux_slirp_pollfds_poll ); diff --git a/src/interface/linux/linux_console.c b/src/interface/linux/linux_console.c index 5294fca79..d5415b61c 100644 --- a/src/interface/linux/linux_console.c +++ b/src/interface/linux/linux_console.c @@ -28,7 +28,7 @@ FILE_LICENCE(GPL2_OR_LATER); #include <ipxe/init.h> #include <ipxe/keys.h> -#include <linux_api.h> +#include <ipxe/linux_api.h> #include <linux/termios.h> #include <asm/errno.h> diff --git a/src/interface/linux/linux_entropy.c b/src/interface/linux/linux_entropy.c index 0f8e45d36..257e993a0 100644 --- a/src/interface/linux/linux_entropy.c +++ b/src/interface/linux/linux_entropy.c @@ -31,7 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <errno.h> -#include <linux_api.h> +#include <ipxe/linux_api.h> #include <ipxe/entropy.h> /** Entropy source filename */ diff --git a/src/interface/linux/linux_nap.c b/src/interface/linux/linux_nap.c index f1d3cd962..3e77bc7f1 100644 --- a/src/interface/linux/linux_nap.c +++ b/src/interface/linux/linux_nap.c @@ -21,7 +21,7 @@ FILE_LICENCE(GPL2_OR_LATER); #include <ipxe/nap.h> -#include <linux_api.h> +#include <ipxe/linux_api.h> /** @file * diff --git a/src/interface/linux/linux_pci.c b/src/interface/linux/linux_pci.c index 0c140cb89..99c629c19 100644 --- a/src/interface/linux/linux_pci.c +++ b/src/interface/linux/linux_pci.c @@ -26,7 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <errno.h> #include <byteswap.h> -#include <linux_api.h> +#include <ipxe/linux_api.h> #include <ipxe/linux.h> #include <ipxe/pci.h> diff --git a/src/interface/linux/linux_smbios.c b/src/interface/linux/linux_smbios.c index 6e5174d23..981873943 100644 --- a/src/interface/linux/linux_smbios.c +++ b/src/interface/linux/linux_smbios.c @@ -20,21 +20,22 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <errno.h> -#include <linux_api.h> +#include <ipxe/linux_api.h> +#include <ipxe/linux_sysfs.h> #include <ipxe/linux.h> +#include <ipxe/umalloc.h> +#include <ipxe/init.h> #include <ipxe/smbios.h> -/** SMBIOS filename */ -static const char smbios_filename[] = "/dev/mem"; - -/** SMBIOS entry point scan region start address */ -#define SMBIOS_ENTRY_START 0xf0000 +/** SMBIOS entry point filename */ +static const char smbios_entry_filename[] = + "/sys/firmware/dmi/tables/smbios_entry_point"; -/** SMBIOS entry point scan region length */ -#define SMBIOS_ENTRY_LEN 0x10000 +/** SMBIOS filename */ +static const char smbios_filename[] = "/sys/firmware/dmi/tables/DMI"; -/** SMBIOS mapping alignment */ -#define SMBIOS_ALIGN 0x1000 +/** Cache SMBIOS data */ +static userptr_t smbios_data; /** * Find SMBIOS @@ -43,73 +44,84 @@ static const char smbios_filename[] = "/dev/mem"; * @ret rc Return status code */ static int linux_find_smbios ( struct smbios *smbios ) { - struct smbios_entry entry; - void *entry_mem; - void *smbios_mem; - size_t smbios_offset; - size_t smbios_indent; - size_t smbios_len; - int fd; + struct smbios3_entry *smbios3_entry; + struct smbios_entry *smbios_entry; + userptr_t entry; + void *data; + int len; int rc; - /* Open SMBIOS file */ - fd = linux_open ( smbios_filename, O_RDONLY ); - if ( fd < 0 ) { - rc = -ELINUX ( linux_errno ); - DBGC ( smbios, "SMBIOS could not open %s: %s\n", - smbios_filename, linux_strerror ( linux_errno ) ); - goto err_open; + /* Read entry point file */ + len = linux_sysfs_read ( smbios_entry_filename, &entry ); + if ( len < 0 ) { + rc = len; + DBGC ( smbios, "SMBIOS could not read %s: %s\n", + smbios_entry_filename, strerror ( rc ) ); + goto err_entry; } - - /* Map the region potentially containing the SMBIOS entry point */ - entry_mem = linux_mmap ( NULL, SMBIOS_ENTRY_LEN, PROT_READ, MAP_SHARED, - fd, SMBIOS_ENTRY_START ); - if ( entry_mem == MAP_FAILED ) { - rc = -ELINUX ( linux_errno ); - DBGC ( smbios, "SMBIOS could not mmap %s (%#x+%#x): %s\n", - smbios_filename, SMBIOS_ENTRY_START, SMBIOS_ENTRY_LEN, - linux_strerror ( linux_errno ) ); - goto err_mmap_entry; + data = user_to_virt ( entry, 0 ); + smbios3_entry = data; + smbios_entry = data; + if ( ( len >= ( ( int ) sizeof ( *smbios3_entry ) ) ) && + ( smbios3_entry->signature == SMBIOS3_SIGNATURE ) ) { + smbios->version = SMBIOS_VERSION ( smbios3_entry->major, + smbios3_entry->minor ); + } else if ( ( len >= ( ( int ) sizeof ( *smbios_entry ) ) ) && + ( smbios_entry->signature == SMBIOS_SIGNATURE ) ) { + smbios->version = SMBIOS_VERSION ( smbios_entry->major, + smbios_entry->minor ); + } else { + DBGC ( smbios, "SMBIOS invalid entry point %s:\n", + smbios_entry_filename ); + DBGC_HDA ( smbios, 0, data, len ); + rc = -EINVAL; + goto err_version; } - /* Scan for the SMBIOS entry point */ - if ( ( rc = find_smbios_entry ( virt_to_user ( entry_mem ), - SMBIOS_ENTRY_LEN, &entry ) ) != 0 ) - goto err_find_entry; - - /* Map the region containing the SMBIOS structures */ - smbios_indent = ( entry.smbios_address & ( SMBIOS_ALIGN - 1 ) ); - smbios_offset = ( entry.smbios_address - smbios_indent ); - smbios_len = ( entry.smbios_len + smbios_indent ); - smbios_mem = linux_mmap ( NULL, smbios_len, PROT_READ, MAP_SHARED, - fd, smbios_offset ); - if ( smbios_mem == MAP_FAILED ) { - rc = -ELINUX ( linux_errno ); - DBGC ( smbios, "SMBIOS could not mmap %s (%#zx+%#zx): %s\n", - smbios_filename, smbios_offset, smbios_len, - linux_strerror ( linux_errno ) ); - goto err_mmap_smbios; + /* Read SMBIOS file */ + len = linux_sysfs_read ( smbios_filename, &smbios_data ); + if ( len < 0 ) { + rc = len; + DBGC ( smbios, "SMBIOS could not read %s: %s\n", + smbios_filename, strerror ( rc ) ); + goto err_read; } - /* Fill in entry point descriptor structure */ - smbios->address = virt_to_user ( smbios_mem + smbios_indent ); - smbios->len = entry.smbios_len; - smbios->count = entry.smbios_count; - smbios->version = SMBIOS_VERSION ( entry.major, entry.minor ); + /* Populate SMBIOS descriptor */ + smbios->address = smbios_data; + smbios->len = len; + smbios->count = 0; - /* Unmap the entry point region (no longer required) */ - linux_munmap ( entry_mem, SMBIOS_ENTRY_LEN ); + /* Free entry point */ + ufree ( entry ); return 0; - linux_munmap ( smbios_mem, smbios_len ); - err_mmap_smbios: - err_find_entry: - linux_munmap ( entry_mem, SMBIOS_ENTRY_LEN ); - err_mmap_entry: - linux_close ( fd ); - err_open: + ufree ( smbios_data ); + err_read: + err_version: + ufree ( entry ); + err_entry: return rc; } +/** + * Free cached SMBIOS data + * + */ +static void linux_smbios_shutdown ( int booting __unused ) { + + /* Clear SMBIOS data pointer */ + smbios_clear(); + + /* Free SMBIOS data */ + ufree ( smbios_data ); +} + +/** SMBIOS shutdown function */ +struct startup_fn linux_smbios_startup_fn __startup_fn ( STARTUP_NORMAL ) = { + .name = "linux_smbios", + .shutdown = linux_smbios_shutdown, +}; + PROVIDE_SMBIOS ( linux, find_smbios, linux_find_smbios ); diff --git a/src/interface/linux/linux_sysfs.c b/src/interface/linux/linux_sysfs.c new file mode 100644 index 000000000..4f0027cd4 --- /dev/null +++ b/src/interface/linux/linux_sysfs.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2021 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <string.h> +#include <errno.h> +#include <ipxe/umalloc.h> +#include <ipxe/linux_api.h> +#include <ipxe/linux.h> +#include <ipxe/linux_sysfs.h> + +/** @file + * + * Linux sysfs files + * + */ + +/** Read blocksize */ +#define LINUX_SYSFS_BLKSIZE 4096 + +/** + * Read file from sysfs + * + * @v filename Filename + * @v data Data to fill in + * @ret len Length read, or negative error + */ +int linux_sysfs_read ( const char *filename, userptr_t *data ) { + userptr_t tmp; + ssize_t read; + size_t len; + int fd; + int rc; + + /* Open file */ + fd = linux_open ( filename, O_RDONLY ); + if ( fd < 0 ) { + rc = -ELINUX ( linux_errno ); + DBGC ( filename, "LINUX could not open %s: %s\n", + filename, linux_strerror ( linux_errno ) ); + goto err_open; + } + + /* Read file */ + for ( *data = UNULL, len = 0 ; ; len += read ) { + + /* (Re)allocate space */ + tmp = urealloc ( *data, ( len + LINUX_SYSFS_BLKSIZE ) ); + if ( ! tmp ) { + rc = -ENOMEM; + goto err_alloc; + } + *data = tmp; + + /* Read from file */ + read = linux_read ( fd, user_to_virt ( *data, len ), + LINUX_SYSFS_BLKSIZE ); + if ( read == 0 ) + break; + if ( read < 0 ) { + DBGC ( filename, "LINUX could not read %s: %s\n", + filename, linux_strerror ( linux_errno ) ); + goto err_read; + } + } + + /* Close file */ + linux_close ( fd ); + + DBGC ( filename, "LINUX read %s\n", filename ); + return len; + + err_read: + err_alloc: + ufree ( *data ); + linux_close ( fd ); + err_open: + return rc; +} diff --git a/src/interface/linux/linux_time.c b/src/interface/linux/linux_time.c index 9e99fe9cd..9d410f8e0 100644 --- a/src/interface/linux/linux_time.c +++ b/src/interface/linux/linux_time.c @@ -32,7 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stddef.h> #include <errno.h> -#include <linux_api.h> +#include <ipxe/linux_api.h> #include <ipxe/time.h> /** diff --git a/src/interface/linux/linux_timer.c b/src/interface/linux/linux_timer.c index 9c5e96f2b..418fd046a 100644 --- a/src/interface/linux/linux_timer.c +++ b/src/interface/linux/linux_timer.c @@ -21,7 +21,7 @@ FILE_LICENCE(GPL2_OR_LATER); #include <stddef.h> #include <ipxe/timer.h> -#include <linux_api.h> +#include <ipxe/linux_api.h> /** @file * diff --git a/src/interface/linux/linux_umalloc.c b/src/interface/linux/linux_umalloc.c index aa0052c53..a7250fa5b 100644 --- a/src/interface/linux/linux_umalloc.c +++ b/src/interface/linux/linux_umalloc.c @@ -29,7 +29,7 @@ FILE_LICENCE(GPL2_OR_LATER); #include <assert.h> #include <ipxe/umalloc.h> -#include <linux_api.h> +#include <ipxe/linux_api.h> /** Special address returned for empty allocations */ #define NOWHERE ((void *)-1) diff --git a/src/interface/linux/linuxprefix.c b/src/interface/linux/linuxprefix.c new file mode 100644 index 000000000..f38236202 --- /dev/null +++ b/src/interface/linux/linuxprefix.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <stdlib.h> +#include <ipxe/linux_api.h> + +/** + * Linux entry point + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +int __asmcall _linux_start ( int argc, char **argv ) { + + /* Store command-line arguments */ + linux_argc = argc; + linux_argv = argv; + + /* Run iPXE */ + return main(); +} |
