2017-12-04 22:08:17 +09:00
/*
* Check decoding of KVM_ * commands of ioctl syscall using / dev / kvm API .
* Based on kvmtest . c from https : //lwn.net/Articles/658512/
*
* kvmtest . c author : Josh Triplett < josh @ joshtriplett . org >
* Copyright ( c ) 2015 Intel Corporation
2018-02-13 22:00:00 +00:00
* Copyright ( c ) 2017 - 2018 The strace developers .
2017-12-04 22:08:17 +09:00
*
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the " Software " ) , to
* deal in the Software without restriction , including without limitation the
* rights to use , copy , modify , merge , publish , distribute , sublicense , and / or
* sell copies of the Software , and to permit persons to whom the Software is
* furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING
* FROM , OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE .
*/
# include "tests.h"
# if defined HAVE_LINUX_KVM_H \
& & defined HAVE_STRUCT_KVM_REGS \
& & defined HAVE_STRUCT_KVM_SREGS \
& & defined HAVE_STRUCT_KVM_USERSPACE_MEMORY_REGION \
& & ( defined __x86_64__ | | defined __i386__ )
# include <fcntl.h>
# include <stdint.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys / ioctl.h>
# include <sys / mman.h>
# include <linux / kvm.h>
static int
kvm_ioctl ( int fd , unsigned long cmd , const char * cmd_str , void * arg )
{
int rc = ioctl ( fd , cmd , arg ) ;
if ( rc < 0 )
perror_msg_and_skip ( " %s " , cmd_str ) ;
return rc ;
}
# define KVM_IOCTL(fd_, cmd_, arg_) \
kvm_ioctl ( ( fd_ ) , ( cmd_ ) , # cmd_ , ( arg_ ) )
static const char dev [ ] = " /dev/kvm " ;
static const char vm_dev [ ] = " anon_inode:kvm-vm " ;
static const char vcpu_dev [ ] = " anon_inode:kvm-vcpu " ;
static size_t page_size ;
2018-02-13 02:38:54 +01:00
extern const char code [ ] ;
extern const unsigned short code_size ;
__asm__ (
" .type code, @object \n "
" code: \n "
" mov $0xd80003f8, %edx \n "
" mov $' \n ', %al \n "
" out %al, (%dx) \n "
" hlt \n "
" .size code, . - code \n "
" .type code_size, @object \n "
" code_size: \n "
" .short . - code \n "
" .size code_size, . - code_size \n "
) ;
2017-12-04 22:08:17 +09:00
static void
run_kvm ( const int vcpu_fd , struct kvm_run * const run , const size_t mmap_size ,
void * const mem )
{
/* Initialize CS to point at 0, via a read-modify-write of sregs. */
struct kvm_sregs sregs ;
KVM_IOCTL ( vcpu_fd , KVM_GET_SREGS , & sregs ) ;
printf ( " ioctl(%d<%s>, KVM_GET_SREGS, {cs={base=%#jx, limit=%u, selector=%u "
" , type=%u, present=%u, dpl=%u, db=%u, s=%u, l=%u, g=%u, avl=%u} "
" , ...}) = 0 \n " , vcpu_fd , vcpu_dev , ( uintmax_t ) sregs . cs . base ,
sregs . cs . limit , sregs . cs . selector , sregs . cs . type ,
sregs . cs . present , sregs . cs . dpl , sregs . cs . db , sregs . cs . s ,
sregs . cs . l , sregs . cs . g , sregs . cs . avl ) ;
sregs . cs . base = 0 ;
sregs . cs . selector = 0 ;
KVM_IOCTL ( vcpu_fd , KVM_SET_SREGS , & sregs ) ;
printf ( " ioctl(%d<%s>, KVM_SET_SREGS, {cs={base=%#jx, limit=%u "
" , selector=%u, type=%u, present=%u, dpl=%u, db=%u, s=%u "
" , l=%u, g=%u, avl=%u}, ...}) = 0 \n " ,
vcpu_fd , vcpu_dev , ( uintmax_t ) sregs . cs . base ,
sregs . cs . limit , sregs . cs . selector , sregs . cs . type ,
sregs . cs . present , sregs . cs . dpl , sregs . cs . db , sregs . cs . s ,
sregs . cs . l , sregs . cs . g , sregs . cs . avl ) ;
/*
* Initialize registers : instruction pointer for our code , addends ,
* and initial flags required by x86 architecture .
*/
struct kvm_regs regs = {
. rip = page_size ,
. rax = 2 ,
. rbx = 2 ,
. rflags = 0x2 ,
} ;
KVM_IOCTL ( vcpu_fd , KVM_SET_REGS , & regs ) ;
printf ( " ioctl(%d<%s>, KVM_SET_REGS, {rax=%#jx, ... "
" , rsp=%#jx, rbp=%#jx, ..., rip=%#jx, rflags=%#jx}) = 0 \n " ,
vcpu_fd , vcpu_dev , ( uintmax_t ) regs . rax ,
( uintmax_t ) regs . rsp , ( uintmax_t ) regs . rbp ,
( uintmax_t ) regs . rip , ( uintmax_t ) regs . rflags ) ;
2018-02-13 02:38:54 +01:00
/* Copy the code */
2017-12-04 22:08:17 +09:00
memcpy ( mem , code , code_size ) ;
const char * p = " \n " ;
/* Repeatedly run code and handle VM exits. */
for ( ; ; ) {
KVM_IOCTL ( vcpu_fd , KVM_RUN , NULL ) ;
printf ( " ioctl(%d<%s>, KVM_RUN, 0) = 0 \n " , vcpu_fd , vcpu_dev ) ;
switch ( run - > exit_reason ) {
case KVM_EXIT_HLT :
if ( p )
error_msg_and_fail ( " premature KVM_EXIT_HLT " ) ;
return ;
case KVM_EXIT_IO :
if ( run - > io . direction = = KVM_EXIT_IO_OUT
& & run - > io . size = = 1
& & run - > io . port = = 0x03f8
& & run - > io . count = = 1
& & run - > io . data_offset < mmap_size
& & p & & * p = = ( ( char * ) run ) [ run - > io . data_offset ] )
p = NULL ;
else
error_msg_and_fail ( " unhandled KVM_EXIT_IO " ) ;
break ;
2018-02-13 02:37:19 +01:00
case KVM_EXIT_MMIO :
error_msg_and_fail ( " Got an unexpected MMIO exit: "
" phys_addr %#llx, "
" data %02x %02x %02x %02x "
" %02x %02x %02x %02x, "
" len %u, is_write %hhu " ,
( unsigned long long ) run - > mmio . phys_addr ,
run - > mmio . data [ 0 ] , run - > mmio . data [ 1 ] ,
run - > mmio . data [ 2 ] , run - > mmio . data [ 3 ] ,
run - > mmio . data [ 4 ] , run - > mmio . data [ 5 ] ,
run - > mmio . data [ 6 ] , run - > mmio . data [ 7 ] ,
run - > mmio . len , run - > mmio . is_write ) ;
2017-12-04 22:08:17 +09:00
default :
error_msg_and_fail ( " exit_reason = %#x " ,
run - > exit_reason ) ;
}
}
}
int
main ( void )
{
skip_if_unavailable ( " /proc/self/fd/ " ) ;
int kvm = open ( dev , O_RDWR ) ;
if ( kvm < 0 )
perror_msg_and_skip ( " open: %s " , dev ) ;
/* Make sure we have the stable version of the API */
int ret = KVM_IOCTL ( kvm , KVM_GET_API_VERSION , 0 ) ;
if ( ret ! = KVM_API_VERSION )
error_msg_and_skip ( " KVM_GET_API_VERSION returned %d "
" , KVM_API_VERSION is %d " ,
kvm , KVM_API_VERSION ) ;
printf ( " ioctl(%d<%s>, KVM_GET_API_VERSION, 0) = %d \n " ,
kvm , dev , ret ) ;
int vm_fd = KVM_IOCTL ( kvm , KVM_CREATE_VM , 0 ) ;
printf ( " ioctl(%d<%s>, KVM_CREATE_VM, 0) = %d<%s> \n " ,
kvm , dev , vm_fd , vm_dev ) ;
/* Allocate one aligned page of guest memory to hold the code. */
page_size = get_page_size ( ) ;
void * const mem = mmap ( NULL , page_size , PROT_READ | PROT_WRITE ,
MAP_SHARED | MAP_ANONYMOUS , - 1 , 0 ) ;
if ( mem = = MAP_FAILED )
perror_msg_and_fail ( " mmap page " ) ;
/* Map it to the second page frame (to avoid the real-mode IDT at 0). */
struct kvm_userspace_memory_region region = {
. slot = 0 ,
. guest_phys_addr = page_size ,
. memory_size = page_size ,
. userspace_addr = ( uintptr_t ) mem ,
} ;
KVM_IOCTL ( vm_fd , KVM_SET_USER_MEMORY_REGION , & region ) ;
printf ( " ioctl(%d<%s>, KVM_SET_USER_MEMORY_REGION "
" , {slot=0, flags=0, guest_phys_addr=%#lx, memory_size=%lu "
" , userspace_addr=%p}) = 0 \n " , vm_fd , vm_dev ,
( unsigned long ) page_size , ( unsigned long ) page_size , mem ) ;
int vcpu_fd = KVM_IOCTL ( vm_fd , KVM_CREATE_VCPU , NULL ) ;
printf ( " ioctl(%d<%s>, KVM_CREATE_VCPU, 0) = %d<%s> \n " ,
vm_fd , vm_dev , vcpu_fd , vcpu_dev ) ;
/* Map the shared kvm_run structure and following data. */
ret = KVM_IOCTL ( kvm , KVM_GET_VCPU_MMAP_SIZE , NULL ) ;
struct kvm_run * run ;
if ( ret < ( int ) sizeof ( * run ) )
error_msg_and_fail ( " KVM_GET_VCPU_MMAP_SIZE returned %d < %d " ,
ret , ( int ) sizeof ( * run ) ) ;
printf ( " ioctl(%d<%s>, KVM_GET_VCPU_MMAP_SIZE, 0) = %d \n " ,
kvm , dev , ret ) ;
const size_t mmap_size = ( ret + page_size - 1 ) & - page_size ;
run = mmap ( NULL , mmap_size , PROT_READ | PROT_WRITE ,
MAP_SHARED , vcpu_fd , 0 ) ;
if ( run = = MAP_FAILED )
perror_msg_and_fail ( " mmap vcpu " ) ;
run_kvm ( vcpu_fd , run , mmap_size , mem ) ;
puts ( " +++ exited with 0 +++ " ) ;
return 0 ;
}
# else /* !HAVE_LINUX_KVM_H */
SKIP_MAIN_UNDEFINED ( " HAVE_LINUX_KVM_H && HAVE_STRUCT_KVM_REGS && "
" HAVE_STRUCT_KVM_SREGS && "
" HAVE_STRUCT_KVM_USERSPACE_MEMORY_REGION && "
" (__x86_64__ || __i386__) " )
# endif