/* * Copyright (C) 2013 Michael Brown . * * 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 #include #include #include /** SMBIOS filename */ static const char smbios_filename[] = "/dev/mem"; /** SMBIOS entry point scan region start address */ #define SMBIOS_ENTRY_START 0xf0000 /** SMBIOS entry point scan region length */ #define SMBIOS_ENTRY_LEN 0x10000 /** SMBIOS mapping alignment */ #define SMBIOS_ALIGN 0x1000 /** * Find SMBIOS * * @v smbios SMBIOS entry point descriptor structure to fill in * @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; 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; } /* 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; } /* 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; } /* 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 ); /* Unmap the entry point region (no longer required) */ linux_munmap ( entry_mem, SMBIOS_ENTRY_LEN ); 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: return rc; } PROVIDE_SMBIOS ( linux, find_smbios, linux_find_smbios );