2012-01-10 21:31:21 +04:00
/*
* virt - host - validate - common . c : Sanity check helper APIs
*
2014-10-28 21:38:04 +03:00
* Copyright ( C ) 2012 , 2014 Red Hat , Inc .
2012-01-10 21:31:21 +04:00
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) any later version .
*
* This library 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
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
2012-09-21 02:30:55 +04:00
* License along with this library . If not , see
2012-07-21 14:06:23 +04:00
* < http : //www.gnu.org/licenses/>.
2012-01-10 21:31:21 +04:00
*
*/
# include <config.h>
# include <stdarg.h>
# include <unistd.h>
# include <sys/utsname.h>
2015-10-07 19:46:18 +03:00
# include <sys/stat.h>
2012-01-10 21:31:21 +04:00
2012-12-12 22:06:53 +04:00
# include "viralloc.h"
2018-09-29 22:37:22 +03:00
# include "vircgroup.h"
2012-01-10 21:31:21 +04:00
# include "virfile.h"
# include "virt-host-validate-common.h"
2013-04-03 14:36:23 +04:00
# include "virstring.h"
2017-08-17 16:48:05 +03:00
# include "virarch.h"
2020-02-17 00:59:15 +03:00
# include "virutil.h"
2012-01-10 21:31:21 +04:00
2013-06-07 19:10:28 +04:00
# define VIR_FROM_THIS VIR_FROM_NONE
2019-03-16 21:20:32 +03:00
VIR_ENUM_IMPL ( virHostValidateCPUFlag ,
VIR_HOST_VALIDATE_CPU_FLAG_LAST ,
2016-03-29 17:38:28 +03:00
" vmx " ,
2016-05-03 09:10:54 +03:00
" svm " ,
2020-06-15 11:28:09 +03:00
" sie " ,
2020-06-15 11:28:10 +03:00
" 158 " ,
" sev " ) ;
2016-03-29 17:38:28 +03:00
2012-01-10 21:31:21 +04:00
static bool quiet ;
void virHostMsgSetQuiet ( bool quietFlag )
{
quiet = quietFlag ;
}
void virHostMsgCheck ( const char * prefix ,
const char * format ,
. . . )
{
va_list args ;
char * msg ;
if ( quiet )
return ;
va_start ( args , format ) ;
2019-10-22 15:11:15 +03:00
msg = g_strdup_vprintf ( format , args ) ;
2012-01-10 21:31:21 +04:00
va_end ( args ) ;
fprintf ( stdout , _ ( " %6s: Checking %-60s: " ) , prefix , msg ) ;
VIR_FREE ( msg ) ;
}
static bool virHostMsgWantEscape ( void )
{
static bool detectTty = true ;
2014-10-28 21:38:04 +03:00
static bool wantEscape ;
2012-01-10 21:31:21 +04:00
if ( detectTty ) {
if ( isatty ( STDOUT_FILENO ) )
wantEscape = true ;
detectTty = false ;
}
return wantEscape ;
}
void virHostMsgPass ( void )
{
if ( quiet )
return ;
if ( virHostMsgWantEscape ( ) )
fprintf ( stdout , " \033 [32m%s \033 [0m \n " , _ ( " PASS " ) ) ;
else
fprintf ( stdout , " %s \n " , _ ( " PASS " ) ) ;
}
static const char * failMessages [ ] = {
N_ ( " FAIL " ) ,
N_ ( " WARN " ) ,
N_ ( " NOTE " ) ,
} ;
2020-01-09 13:39:55 +03:00
G_STATIC_ASSERT ( G_N_ELEMENTS ( failMessages ) = = VIR_HOST_VALIDATE_LAST ) ;
2012-01-10 21:31:21 +04:00
static const char * failEscapeCodes [ ] = {
" \033 [31m " ,
" \033 [33m " ,
" \033 [34m " ,
} ;
2020-01-09 13:39:55 +03:00
G_STATIC_ASSERT ( G_N_ELEMENTS ( failEscapeCodes ) = = VIR_HOST_VALIDATE_LAST ) ;
2012-01-10 21:31:21 +04:00
void virHostMsgFail ( virHostValidateLevel level ,
2015-10-07 19:36:37 +03:00
const char * format ,
. . . )
2012-01-10 21:31:21 +04:00
{
2015-10-07 19:36:37 +03:00
va_list args ;
char * msg ;
if ( quiet )
return ;
va_start ( args , format ) ;
2019-10-22 15:11:15 +03:00
msg = g_strdup_vprintf ( format , args ) ;
2015-10-07 19:36:37 +03:00
va_end ( args ) ;
2012-01-10 21:31:21 +04:00
if ( virHostMsgWantEscape ( ) )
fprintf ( stdout , " %s%s \033 [0m (%s) \n " ,
2015-10-07 19:36:37 +03:00
failEscapeCodes [ level ] , _ ( failMessages [ level ] ) , msg ) ;
2012-01-10 21:31:21 +04:00
else
fprintf ( stdout , " %s (%s) \n " ,
2015-10-07 19:36:37 +03:00
_ ( failMessages [ level ] ) , msg ) ;
VIR_FREE ( msg ) ;
2012-01-10 21:31:21 +04:00
}
2015-10-07 19:02:31 +03:00
int virHostValidateDeviceExists ( const char * hvname ,
const char * dev_name ,
virHostValidateLevel level ,
const char * hint )
2012-01-10 21:31:21 +04:00
{
2015-10-07 19:02:31 +03:00
virHostMsgCheck ( hvname , " if device %s exists " , dev_name ) ;
if ( access ( dev_name , F_OK ) < 0 ) {
2015-10-07 19:36:37 +03:00
virHostMsgFail ( level , " %s " , hint ) ;
2015-10-07 19:02:31 +03:00
return - 1 ;
}
virHostMsgPass ( ) ;
return 0 ;
}
int virHostValidateDeviceAccessible ( const char * hvname ,
const char * dev_name ,
virHostValidateLevel level ,
const char * hint )
{
virHostMsgCheck ( hvname , " if device %s is accessible " , dev_name ) ;
2012-01-10 21:31:21 +04:00
2012-03-30 03:08:27 +04:00
if ( access ( dev_name , R_OK | W_OK ) < 0 ) {
2015-10-07 19:36:37 +03:00
virHostMsgFail ( level , " %s " , hint ) ;
2012-01-10 21:31:21 +04:00
return - 1 ;
}
virHostMsgPass ( ) ;
return 0 ;
}
2015-10-07 18:58:39 +03:00
int virHostValidateNamespace ( const char * hvname ,
const char * ns_name ,
virHostValidateLevel level ,
const char * hint )
{
char nspath [ 100 ] ;
2020-08-03 18:27:58 +03:00
virHostMsgCheck ( hvname , " for namespace %s " , ns_name ) ;
2019-11-13 16:53:42 +03:00
g_snprintf ( nspath , sizeof ( nspath ) , " /proc/self/ns/%s " , ns_name ) ;
2015-10-07 18:58:39 +03:00
if ( access ( nspath , F_OK ) < 0 ) {
2015-10-07 19:36:37 +03:00
virHostMsgFail ( level , " %s " , hint ) ;
2015-10-07 18:58:39 +03:00
return - 1 ;
}
virHostMsgPass ( ) ;
return 0 ;
}
2016-03-29 17:38:28 +03:00
virBitmapPtr virHostValidateGetCPUFlags ( void )
2012-01-10 21:31:21 +04:00
{
2016-03-29 17:38:28 +03:00
FILE * fp ;
2016-04-14 16:55:21 +03:00
virBitmapPtr flags = NULL ;
2012-01-10 21:31:21 +04:00
2016-03-29 17:38:28 +03:00
if ( ! ( fp = fopen ( " /proc/cpuinfo " , " r " ) ) )
return NULL ;
2020-10-01 17:56:48 +03:00
flags = virBitmapNew ( VIR_HOST_VALIDATE_CPU_FLAG_LAST ) ;
2012-01-10 21:31:21 +04:00
do {
char line [ 1024 ] ;
2016-03-29 17:38:28 +03:00
char * start ;
char * * tokens ;
size_t ntokens ;
size_t i ;
2012-01-10 21:31:21 +04:00
if ( ! fgets ( line , sizeof ( line ) , fp ) )
break ;
2016-05-03 09:10:52 +03:00
/* The line we're interested in is marked differently depending
* on the architecture , so check possible prefixes */
if ( ! STRPREFIX ( line , " flags " ) & &
! STRPREFIX ( line , " Features " ) & &
2020-06-15 11:28:09 +03:00
! STRPREFIX ( line , " features " ) & &
! STRPREFIX ( line , " facilities " ) )
2016-03-29 17:38:28 +03:00
continue ;
/* fgets() includes the trailing newline in the output buffer,
* so we need to clean that up ourselves . We can safely access
* line [ strlen ( line ) - 1 ] because the checks above would cause
* us to skip empty strings */
line [ strlen ( line ) - 1 ] = ' \0 ' ;
/* Skip to the separator */
if ( ! ( start = strchr ( line , ' : ' ) ) )
continue ;
/* Split the line using " " as a delimiter. The first token
* will always be " : " , but that ' s okay */
if ( ! ( tokens = virStringSplitCount ( start , " " , 0 , & ntokens ) ) )
continue ;
/* Go through all flags and check whether one of those we
* might want to check for later on is present ; if that ' s
* the case , set the relevant bit in the bitmap */
for ( i = 0 ; i < ntokens ; i + + ) {
int value ;
if ( ( value = virHostValidateCPUFlagTypeFromString ( tokens [ i ] ) ) > = 0 )
ignore_value ( virBitmapSetBit ( flags , value ) ) ;
2012-01-10 21:31:21 +04:00
}
2016-03-29 17:38:28 +03:00
2016-11-25 11:18:35 +03:00
virStringListFreeCount ( tokens , ntokens ) ;
2012-01-10 21:31:21 +04:00
} while ( 1 ) ;
VIR_FORCE_FCLOSE ( fp ) ;
2016-03-29 17:38:28 +03:00
return flags ;
2012-01-10 21:31:21 +04:00
}
int virHostValidateLinuxKernel ( const char * hvname ,
int version ,
virHostValidateLevel level ,
const char * hint )
{
struct utsname uts ;
unsigned long thisversion ;
uname ( & uts ) ;
virHostMsgCheck ( hvname , _ ( " for Linux >= %d.%d.%d " ) ,
( ( version > > 16 ) & 0xff ) ,
( ( version > > 8 ) & 0xff ) ,
( version & 0xff ) ) ;
if ( STRNEQ ( uts . sysname , " Linux " ) ) {
2015-10-07 19:36:37 +03:00
virHostMsgFail ( level , " %s " , hint ) ;
2012-01-10 21:31:21 +04:00
return - 1 ;
}
if ( virParseVersionString ( uts . release , & thisversion , true ) < 0 ) {
2015-10-07 19:36:37 +03:00
virHostMsgFail ( level , " %s " , hint ) ;
2012-01-10 21:31:21 +04:00
return - 1 ;
}
if ( thisversion < version ) {
2015-10-07 19:36:37 +03:00
virHostMsgFail ( level , " %s " , hint ) ;
2012-01-10 21:31:21 +04:00
return - 1 ;
} else {
virHostMsgPass ( ) ;
return 0 ;
}
}
2015-10-07 19:36:37 +03:00
2018-09-29 22:37:22 +03:00
# ifdef __linux__
int virHostValidateCGroupControllers ( const char * hvname ,
int controllers ,
virHostValidateLevel level )
2015-10-07 19:36:37 +03:00
{
2020-09-22 13:52:38 +03:00
g_autoptr ( virCgroup ) group = NULL ;
2018-09-29 22:37:22 +03:00
int ret = 0 ;
size_t i ;
2015-10-07 19:36:37 +03:00
2018-09-29 22:37:22 +03:00
if ( virCgroupNewSelf ( & group ) < 0 )
return - 1 ;
2015-10-07 19:36:37 +03:00
2018-09-29 22:37:22 +03:00
for ( i = 0 ; i < VIR_CGROUP_CONTROLLER_LAST ; i + + ) {
int flag = 1 < < i ;
const char * cg_name = virCgroupControllerTypeToString ( i ) ;
2016-04-04 17:25:15 +03:00
2018-09-29 22:37:22 +03:00
if ( ! ( controllers & flag ) )
2015-10-07 19:36:37 +03:00
continue ;
2018-09-29 22:37:22 +03:00
virHostMsgCheck ( hvname , " for cgroup '%s' controller support " , cg_name ) ;
2015-10-07 19:36:37 +03:00
2018-09-29 22:37:22 +03:00
if ( ! virCgroupHasController ( group , i ) ) {
ret = - 1 ;
virHostMsgFail ( level , " Enable '%s' in kernel Kconfig file or "
" mount/enable cgroup controller in your system " ,
cg_name ) ;
} else {
virHostMsgPass ( ) ;
2016-04-04 17:25:15 +03:00
}
2015-10-07 19:36:37 +03:00
}
2018-09-29 22:37:22 +03:00
return ret ;
2015-10-07 19:36:37 +03:00
}
2018-09-29 22:37:22 +03:00
# else /* !__linux__ */
2019-10-14 15:44:29 +03:00
int virHostValidateCGroupControllers ( const char * hvname G_GNUC_UNUSED ,
int controllers G_GNUC_UNUSED ,
2018-09-29 22:37:22 +03:00
virHostValidateLevel level )
2015-10-12 16:33:04 +03:00
{
virHostMsgFail ( level , " %s " , " This platform does not support cgroups " ) ;
return - 1 ;
}
2018-09-29 22:37:22 +03:00
# endif /* !__linux__ */
2015-10-07 19:46:18 +03:00
int virHostValidateIOMMU ( const char * hvname ,
virHostValidateLevel level )
{
2016-03-29 17:38:28 +03:00
virBitmapPtr flags ;
2015-10-07 19:46:18 +03:00
struct stat sb ;
const char * bootarg = NULL ;
2019-03-01 14:10:26 +03:00
bool isAMD = false , isIntel = false ;
virArch arch = virArchFromHost ( ) ;
struct dirent * dent ;
int rc ;
2016-03-29 17:38:28 +03:00
flags = virHostValidateGetCPUFlags ( ) ;
if ( flags & & virBitmapIsBitSet ( flags , VIR_HOST_VALIDATE_CPU_FLAG_VMX ) )
2015-10-07 19:46:18 +03:00
isIntel = true ;
2016-03-29 17:38:28 +03:00
else if ( flags & & virBitmapIsBitSet ( flags , VIR_HOST_VALIDATE_CPU_FLAG_SVM ) )
2015-10-07 19:46:18 +03:00
isAMD = true ;
2016-03-29 17:38:28 +03:00
virBitmapFree ( flags ) ;
2015-10-07 19:46:18 +03:00
if ( isIntel ) {
2017-08-17 16:48:05 +03:00
virHostMsgCheck ( hvname , " %s " , _ ( " for device assignment IOMMU support " ) ) ;
2015-10-07 19:46:18 +03:00
if ( access ( " /sys/firmware/acpi/tables/DMAR " , F_OK ) = = 0 ) {
virHostMsgPass ( ) ;
bootarg = " intel_iommu=on " ;
} else {
virHostMsgFail ( level ,
" No ACPI DMAR table found, IOMMU either "
" disabled in BIOS or not supported by this "
" hardware platform " ) ;
return - 1 ;
}
} else if ( isAMD ) {
2017-08-17 16:48:05 +03:00
virHostMsgCheck ( hvname , " %s " , _ ( " for device assignment IOMMU support " ) ) ;
2015-10-07 19:46:18 +03:00
if ( access ( " /sys/firmware/acpi/tables/IVRS " , F_OK ) = = 0 ) {
virHostMsgPass ( ) ;
bootarg = " iommu=pt iommu=1 " ;
} else {
virHostMsgFail ( level ,
" No ACPI IVRS table found, IOMMU either "
" disabled in BIOS or not supported by this "
" hardware platform " ) ;
return - 1 ;
}
2019-03-01 14:10:26 +03:00
} else if ( ARCH_IS_PPC64 ( arch ) ) {
2017-08-17 16:48:05 +03:00
/* Empty Block */
2019-03-01 14:10:26 +03:00
} else if ( ARCH_IS_S390 ( arch ) ) {
2020-10-26 00:50:51 +03:00
g_autoptr ( DIR ) dir = NULL ;
2020-10-26 00:56:22 +03:00
2019-03-01 14:10:26 +03:00
/* On s390x, we skip the IOMMU check if there are no PCI
* devices ( which is quite usual on s390x ) . If there are
* no PCI devices the directory is still there but is
* empty . */
if ( ! virDirOpen ( & dir , " /sys/bus/pci/devices " ) )
return 0 ;
rc = virDirRead ( dir , & dent , NULL ) ;
if ( rc < = 0 )
return 0 ;
2015-10-07 19:46:18 +03:00
} else {
virHostMsgFail ( level ,
" Unknown if this platform has IOMMU support " ) ;
return - 1 ;
}
/* We can only check on newer kernels with iommu groups & vfio */
if ( stat ( " /sys/kernel/iommu_groups " , & sb ) < 0 )
return 0 ;
if ( ! S_ISDIR ( sb . st_mode ) )
return 0 ;
virHostMsgCheck ( hvname , " %s " , _ ( " if IOMMU is enabled by kernel " ) ) ;
if ( sb . st_nlink < = 2 ) {
2019-03-01 14:10:26 +03:00
if ( bootarg )
2017-08-17 16:48:05 +03:00
virHostMsgFail ( level ,
" IOMMU appears to be disabled in kernel. "
" Add %s to kernel cmdline arguments " , bootarg ) ;
else
virHostMsgFail ( level , " IOMMU capability not compiled into kernel. " ) ;
2015-10-07 19:46:18 +03:00
return - 1 ;
}
virHostMsgPass ( ) ;
return 0 ;
}
2020-03-13 20:48:03 +03:00
bool virHostKernelModuleIsLoaded ( const char * module )
{
FILE * fp ;
bool ret = false ;
if ( ! ( fp = fopen ( " /proc/modules " , " r " ) ) )
return false ;
do {
char line [ 1024 ] ;
if ( ! fgets ( line , sizeof ( line ) , fp ) )
break ;
if ( STRPREFIX ( line , module ) ) {
ret = true ;
break ;
}
} while ( 1 ) ;
VIR_FORCE_FCLOSE ( fp ) ;
return ret ;
}
2020-06-15 11:28:09 +03:00
int virHostValidateSecureGuests ( const char * hvname ,
virHostValidateLevel level )
{
virBitmapPtr flags ;
bool hasFac158 = false ;
2020-06-15 11:28:10 +03:00
bool hasAMDSev = false ;
2020-06-15 11:28:09 +03:00
virArch arch = virArchFromHost ( ) ;
g_autofree char * cmdline = NULL ;
static const char * kIBMValues [ ] = { " y " , " Y " , " on " , " ON " , " oN " , " On " , " 1 " } ;
2020-06-15 11:28:10 +03:00
g_autofree char * mod_value = NULL ;
2020-06-15 11:28:09 +03:00
flags = virHostValidateGetCPUFlags ( ) ;
if ( flags & & virBitmapIsBitSet ( flags , VIR_HOST_VALIDATE_CPU_FLAG_FACILITY_158 ) )
hasFac158 = true ;
2020-06-15 11:28:10 +03:00
else if ( flags & & virBitmapIsBitSet ( flags , VIR_HOST_VALIDATE_CPU_FLAG_SEV ) )
hasAMDSev = true ;
2020-06-15 11:28:09 +03:00
virBitmapFree ( flags ) ;
virHostMsgCheck ( hvname , " %s " , _ ( " for secure guest support " ) ) ;
if ( ARCH_IS_S390 ( arch ) ) {
if ( hasFac158 ) {
if ( ! virFileIsDir ( " /sys/firmware/uv " ) ) {
virHostMsgFail ( level , " IBM Secure Execution not supported by "
" the currently used kernel " ) ;
return 0 ;
}
if ( virFileReadValueString ( & cmdline , " /proc/cmdline " ) < 0 )
return - 1 ;
/* we're prefix matching rather than equality matching here, because
* kernel would treat even something like prot_virt = ' yFOO ' as
* enabled
*/
if ( virKernelCmdlineMatchParam ( cmdline , " prot_virt " , kIBMValues ,
G_N_ELEMENTS ( kIBMValues ) ,
VIR_KERNEL_CMDLINE_FLAGS_SEARCH_FIRST |
VIR_KERNEL_CMDLINE_FLAGS_CMP_PREFIX ) ) {
virHostMsgPass ( ) ;
return 1 ;
} else {
virHostMsgFail ( level ,
" IBM Secure Execution appears to be disabled "
" in kernel. Add prot_virt=1 to kernel cmdline "
" arguments " ) ;
}
} else {
virHostMsgFail ( level , " Hardware or firmware does not provide "
" support for IBM Secure Execution " ) ;
}
2020-06-15 11:28:10 +03:00
} else if ( hasAMDSev ) {
if ( virFileReadValueString ( & mod_value , " /sys/module/kvm_amd/parameters/sev " ) < 0 ) {
virHostMsgFail ( level , " AMD Secure Encrypted Virtualization not "
" supported by the currently used kernel " ) ;
return 0 ;
}
if ( mod_value [ 0 ] ! = ' 1 ' ) {
virHostMsgFail ( level ,
" AMD Secure Encrypted Virtualization appears to be "
" disabled in kernel. Add kvm_amd.sev=1 "
" to the kernel cmdline arguments " ) ;
return 0 ;
}
if ( virFileExists ( " /dev/sev " ) ) {
virHostMsgPass ( ) ;
return 1 ;
} else {
virHostMsgFail ( level ,
" AMD Secure Encrypted Virtualization appears to be "
" disabled in firemare. " ) ;
}
2020-06-15 11:28:09 +03:00
} else {
virHostMsgFail ( level ,
" Unknown if this platform has Secure Guest support " ) ;
return - 1 ;
}
return 0 ;
}