2012-02-09 21:25:55 +04:00
/******************************************************************************
* Intel Management Engine Interface ( Intel MEI ) Linux driver
* Intel MEI Interface Header
*
* This file is provided under a dual BSD / GPLv2 license . When using or
* redistributing this file , you may do so under either license .
*
* GPL LICENSE SUMMARY
*
* Copyright ( c ) 2012 Intel Corporation . All rights reserved .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation .
*
* 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 ,
* USA
*
* The full GNU General Public License is included in this distribution
* in the file called LICENSE . GPL .
*
* Contact Information :
* Intel Corporation .
* linux - mei @ linux . intel . com
* http : //www.intel.com
*
* BSD LICENSE
*
* Copyright ( c ) 2003 - 2012 Intel Corporation . All rights reserved .
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
*
* * Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in
* the documentation and / or other materials provided with the
* distribution .
* * Neither the name Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
* SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT
* LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <fcntl.h>
# include <sys/ioctl.h>
# include <unistd.h>
# include <errno.h>
# include <stdint.h>
# include <stdbool.h>
# include <bits/wordsize.h>
2012-05-09 17:38:59 +04:00
# include <linux/mei.h>
2012-02-09 21:25:55 +04:00
/*****************************************************************************
2012-03-12 18:18:09 +04:00
* Intel Management Engine Interface
2012-02-09 21:25:55 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# define mei_msg(_me, fmt, ARGS...) do { \
if ( _me - > verbose ) \
fprintf ( stderr , fmt , # # ARGS ) ; \
} while ( 0 )
# define mei_err(_me, fmt, ARGS...) do { \
fprintf ( stderr , " Error: " fmt , # # ARGS ) ; \
} while ( 0 )
struct mei {
uuid_le guid ;
bool initialized ;
bool verbose ;
unsigned int buf_size ;
unsigned char prot_ver ;
int fd ;
} ;
static void mei_deinit ( struct mei * cl )
{
if ( cl - > fd ! = - 1 )
close ( cl - > fd ) ;
cl - > fd = - 1 ;
cl - > buf_size = 0 ;
cl - > prot_ver = 0 ;
cl - > initialized = false ;
}
static bool mei_init ( struct mei * me , const uuid_le * guid ,
unsigned char req_protocol_version , bool verbose )
{
int result ;
struct mei_client * cl ;
struct mei_connect_client_data data ;
me - > verbose = verbose ;
2019-01-24 15:45:03 +03:00
me - > fd = open ( " /dev/mei0 " , O_RDWR ) ;
2012-02-09 21:25:55 +04:00
if ( me - > fd = = - 1 ) {
mei_err ( me , " Cannot establish a handle to the Intel MEI driver \n " ) ;
goto err ;
}
memcpy ( & me - > guid , guid , sizeof ( * guid ) ) ;
memset ( & data , 0 , sizeof ( data ) ) ;
me - > initialized = true ;
memcpy ( & data . in_client_uuid , & me - > guid , sizeof ( me - > guid ) ) ;
result = ioctl ( me - > fd , IOCTL_MEI_CONNECT_CLIENT , & data ) ;
if ( result ) {
mei_err ( me , " IOCTL_MEI_CONNECT_CLIENT receive message. err=%d \n " , result ) ;
goto err ;
}
cl = & data . out_client_properties ;
mei_msg ( me , " max_message_length %d \n " , cl - > max_msg_length ) ;
mei_msg ( me , " protocol_version %d \n " , cl - > protocol_version ) ;
if ( ( req_protocol_version > 0 ) & &
( cl - > protocol_version ! = req_protocol_version ) ) {
mei_err ( me , " Intel MEI protocol version not supported \n " ) ;
goto err ;
}
me - > buf_size = cl - > max_msg_length ;
me - > prot_ver = cl - > protocol_version ;
return true ;
err :
mei_deinit ( me ) ;
return false ;
}
static ssize_t mei_recv_msg ( struct mei * me , unsigned char * buffer ,
ssize_t len , unsigned long timeout )
{
2021-08-01 10:25:32 +03:00
struct timeval tv ;
fd_set set ;
2012-02-09 21:25:55 +04:00
ssize_t rc ;
2021-08-01 10:25:32 +03:00
tv . tv_sec = timeout / 1000 ;
tv . tv_usec = ( timeout % 1000 ) * 1000000 ;
2012-02-09 21:25:55 +04:00
mei_msg ( me , " call read length = %zd \n " , len ) ;
2021-08-01 10:25:32 +03:00
FD_ZERO ( & set ) ;
FD_SET ( me - > fd , & set ) ;
rc = select ( me - > fd + 1 , & set , NULL , NULL , & tv ) ;
if ( rc > 0 & & FD_ISSET ( me - > fd , & set ) ) {
mei_msg ( me , " have reply \n " ) ;
} else if ( rc = = 0 ) {
rc = - 1 ;
mei_err ( me , " read failed on timeout \n " ) ;
goto out ;
} else { /* rc < 0 */
rc = errno ;
mei_err ( me , " read failed on select with status %zd %s \n " ,
rc , strerror ( errno ) ) ;
goto out ;
}
2012-02-09 21:25:55 +04:00
rc = read ( me - > fd , buffer , len ) ;
if ( rc < 0 ) {
mei_err ( me , " read failed with status %zd %s \n " ,
rc , strerror ( errno ) ) ;
2021-08-01 10:25:32 +03:00
goto out ;
2012-02-09 21:25:55 +04:00
}
2021-08-01 10:25:32 +03:00
mei_msg ( me , " read succeeded with result %zd \n " , rc ) ;
out :
if ( rc < 0 )
mei_deinit ( me ) ;
2012-02-09 21:25:55 +04:00
return rc ;
}
static ssize_t mei_send_msg ( struct mei * me , const unsigned char * buffer ,
ssize_t len , unsigned long timeout )
{
ssize_t written ;
ssize_t rc ;
mei_msg ( me , " call write length = %zd \n " , len ) ;
written = write ( me - > fd , buffer , len ) ;
if ( written < 0 ) {
rc = - errno ;
mei_err ( me , " write failed with status %zd %s \n " ,
written , strerror ( errno ) ) ;
goto out ;
}
2021-08-01 10:25:32 +03:00
mei_msg ( me , " write success \n " ) ;
2012-02-09 21:25:55 +04:00
rc = written ;
out :
if ( rc < 0 )
mei_deinit ( me ) ;
return rc ;
}
/***************************************************************************
2012-11-08 16:57:35 +04:00
* Intel Advanced Management Technology ME Client
2012-02-09 21:25:55 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# define AMT_MAJOR_VERSION 1
# define AMT_MINOR_VERSION 1
# define AMT_STATUS_SUCCESS 0x0
# define AMT_STATUS_INTERNAL_ERROR 0x1
# define AMT_STATUS_NOT_READY 0x2
# define AMT_STATUS_INVALID_AMT_MODE 0x3
# define AMT_STATUS_INVALID_MESSAGE_LENGTH 0x4
# define AMT_STATUS_HOST_IF_EMPTY_RESPONSE 0x4000
# define AMT_STATUS_SDK_RESOURCES 0x1004
# define AMT_BIOS_VERSION_LEN 65
# define AMT_VERSIONS_NUMBER 50
# define AMT_UNICODE_STRING_LEN 20
struct amt_unicode_string {
uint16_t length ;
char string [ AMT_UNICODE_STRING_LEN ] ;
} __attribute__ ( ( packed ) ) ;
struct amt_version_type {
struct amt_unicode_string description ;
struct amt_unicode_string version ;
} __attribute__ ( ( packed ) ) ;
struct amt_version {
uint8_t major ;
uint8_t minor ;
} __attribute__ ( ( packed ) ) ;
struct amt_code_versions {
uint8_t bios [ AMT_BIOS_VERSION_LEN ] ;
uint32_t count ;
struct amt_version_type versions [ AMT_VERSIONS_NUMBER ] ;
} __attribute__ ( ( packed ) ) ;
/***************************************************************************
2012-11-08 16:57:35 +04:00
* Intel Advanced Management Technology Host Interface
2012-02-09 21:25:55 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
struct amt_host_if_msg_header {
struct amt_version version ;
uint16_t _reserved ;
uint32_t command ;
uint32_t length ;
} __attribute__ ( ( packed ) ) ;
struct amt_host_if_resp_header {
struct amt_host_if_msg_header header ;
uint32_t status ;
2020-05-28 17:35:11 +03:00
unsigned char data [ ] ;
2012-02-09 21:25:55 +04:00
} __attribute__ ( ( packed ) ) ;
const uuid_le MEI_IAMTHIF = UUID_LE ( 0x12f80028 , 0xb4b7 , 0x4b2d , \
0xac , 0xa8 , 0x46 , 0xe0 , 0xff , 0x65 , 0x81 , 0x4c ) ;
# define AMT_HOST_IF_CODE_VERSIONS_REQUEST 0x0400001A
# define AMT_HOST_IF_CODE_VERSIONS_RESPONSE 0x0480001A
const struct amt_host_if_msg_header CODE_VERSION_REQ = {
. version = { AMT_MAJOR_VERSION , AMT_MINOR_VERSION } ,
. _reserved = 0 ,
. command = AMT_HOST_IF_CODE_VERSIONS_REQUEST ,
. length = 0
} ;
struct amt_host_if {
struct mei mei_cl ;
unsigned long send_timeout ;
bool initialized ;
} ;
2012-02-20 16:46:03 +04:00
static bool amt_host_if_init ( struct amt_host_if * acmd ,
2012-02-09 21:25:55 +04:00
unsigned long send_timeout , bool verbose )
{
acmd - > send_timeout = ( send_timeout ) ? send_timeout : 20000 ;
acmd - > initialized = mei_init ( & acmd - > mei_cl , & MEI_IAMTHIF , 0 , verbose ) ;
return acmd - > initialized ;
}
2012-02-20 16:46:03 +04:00
static void amt_host_if_deinit ( struct amt_host_if * acmd )
2012-02-09 21:25:55 +04:00
{
mei_deinit ( & acmd - > mei_cl ) ;
acmd - > initialized = false ;
}
2012-02-20 16:46:03 +04:00
static uint32_t amt_verify_code_versions ( const struct amt_host_if_resp_header * resp )
2012-02-09 21:25:55 +04:00
{
uint32_t status = AMT_STATUS_SUCCESS ;
struct amt_code_versions * code_ver ;
size_t code_ver_len ;
uint32_t ver_type_cnt ;
uint32_t len ;
uint32_t i ;
code_ver = ( struct amt_code_versions * ) resp - > data ;
/* length - sizeof(status) */
code_ver_len = resp - > header . length - sizeof ( uint32_t ) ;
ver_type_cnt = code_ver_len -
sizeof ( code_ver - > bios ) -
sizeof ( code_ver - > count ) ;
if ( code_ver - > count ! = ver_type_cnt / sizeof ( struct amt_version_type ) ) {
status = AMT_STATUS_INTERNAL_ERROR ;
goto out ;
}
for ( i = 0 ; i < code_ver - > count ; i + + ) {
len = code_ver - > versions [ i ] . description . length ;
if ( len > AMT_UNICODE_STRING_LEN ) {
status = AMT_STATUS_INTERNAL_ERROR ;
goto out ;
}
len = code_ver - > versions [ i ] . version . length ;
if ( code_ver - > versions [ i ] . version . string [ len ] ! = ' \0 ' | |
len ! = strlen ( code_ver - > versions [ i ] . version . string ) ) {
status = AMT_STATUS_INTERNAL_ERROR ;
goto out ;
}
}
out :
return status ;
}
2012-02-20 16:46:03 +04:00
static uint32_t amt_verify_response_header ( uint32_t command ,
2012-02-09 21:25:55 +04:00
const struct amt_host_if_msg_header * resp_hdr ,
uint32_t response_size )
{
if ( response_size < sizeof ( struct amt_host_if_resp_header ) ) {
return AMT_STATUS_INTERNAL_ERROR ;
} else if ( response_size ! = ( resp_hdr - > length +
sizeof ( struct amt_host_if_msg_header ) ) ) {
return AMT_STATUS_INTERNAL_ERROR ;
} else if ( resp_hdr - > command ! = command ) {
return AMT_STATUS_INTERNAL_ERROR ;
} else if ( resp_hdr - > _reserved ! = 0 ) {
return AMT_STATUS_INTERNAL_ERROR ;
} else if ( resp_hdr - > version . major ! = AMT_MAJOR_VERSION | |
resp_hdr - > version . minor < AMT_MINOR_VERSION ) {
return AMT_STATUS_INTERNAL_ERROR ;
}
return AMT_STATUS_SUCCESS ;
}
static uint32_t amt_host_if_call ( struct amt_host_if * acmd ,
const unsigned char * command , ssize_t command_sz ,
uint8_t * * read_buf , uint32_t rcmd ,
unsigned int expected_sz )
{
uint32_t in_buf_sz ;
2018-10-01 19:44:41 +03:00
ssize_t out_buf_sz ;
2012-02-09 21:25:55 +04:00
ssize_t written ;
uint32_t status ;
struct amt_host_if_resp_header * msg_hdr ;
in_buf_sz = acmd - > mei_cl . buf_size ;
* read_buf = ( uint8_t * ) malloc ( sizeof ( uint8_t ) * in_buf_sz ) ;
if ( * read_buf = = NULL )
return AMT_STATUS_SDK_RESOURCES ;
memset ( * read_buf , 0 , in_buf_sz ) ;
msg_hdr = ( struct amt_host_if_resp_header * ) * read_buf ;
written = mei_send_msg ( & acmd - > mei_cl ,
command , command_sz , acmd - > send_timeout ) ;
if ( written ! = command_sz )
return AMT_STATUS_INTERNAL_ERROR ;
out_buf_sz = mei_recv_msg ( & acmd - > mei_cl , * read_buf , in_buf_sz , 2000 ) ;
if ( out_buf_sz < = 0 )
return AMT_STATUS_HOST_IF_EMPTY_RESPONSE ;
status = msg_hdr - > status ;
if ( status ! = AMT_STATUS_SUCCESS )
return status ;
status = amt_verify_response_header ( rcmd ,
& msg_hdr - > header , out_buf_sz ) ;
if ( status ! = AMT_STATUS_SUCCESS )
return status ;
if ( expected_sz & & expected_sz ! = out_buf_sz )
return AMT_STATUS_INTERNAL_ERROR ;
return AMT_STATUS_SUCCESS ;
}
2012-02-20 16:46:03 +04:00
static uint32_t amt_get_code_versions ( struct amt_host_if * cmd ,
2012-02-09 21:25:55 +04:00
struct amt_code_versions * versions )
{
struct amt_host_if_resp_header * response = NULL ;
uint32_t status ;
status = amt_host_if_call ( cmd ,
( const unsigned char * ) & CODE_VERSION_REQ ,
sizeof ( CODE_VERSION_REQ ) ,
( uint8_t * * ) & response ,
AMT_HOST_IF_CODE_VERSIONS_RESPONSE , 0 ) ;
if ( status ! = AMT_STATUS_SUCCESS )
goto out ;
status = amt_verify_code_versions ( response ) ;
if ( status ! = AMT_STATUS_SUCCESS )
goto out ;
memcpy ( versions , response - > data , sizeof ( struct amt_code_versions ) ) ;
out :
if ( response ! = NULL )
free ( response ) ;
return status ;
}
/************************** end of amt_host_if_command ***********************/
int main ( int argc , char * * argv )
{
struct amt_code_versions ver ;
struct amt_host_if acmd ;
unsigned int i ;
uint32_t status ;
int ret ;
bool verbose ;
verbose = ( argc > 1 & & strcmp ( argv [ 1 ] , " -v " ) = = 0 ) ;
if ( ! amt_host_if_init ( & acmd , 5000 , verbose ) ) {
ret = 1 ;
goto out ;
}
status = amt_get_code_versions ( & acmd , & ver ) ;
2012-02-20 16:46:03 +04:00
amt_host_if_deinit ( & acmd ) ;
2012-02-09 21:25:55 +04:00
switch ( status ) {
case AMT_STATUS_HOST_IF_EMPTY_RESPONSE :
printf ( " Intel AMT: DISABLED \n " ) ;
ret = 0 ;
break ;
case AMT_STATUS_SUCCESS :
printf ( " Intel AMT: ENABLED \n " ) ;
for ( i = 0 ; i < ver . count ; i + + ) {
printf ( " %s: \t %s \n " , ver . versions [ i ] . description . string ,
ver . versions [ i ] . version . string ) ;
}
ret = 0 ;
break ;
default :
printf ( " An error has occurred \n " ) ;
ret = 1 ;
break ;
}
out :
return ret ;
}