2016-07-19 04:54:17 +03:00
/*
* Copyright Gavin Shan , IBM Corporation 2016.
*
* 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
* ( at your option ) any later version .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/etherdevice.h>
# include <linux/netdevice.h>
# include <linux/skbuff.h>
# include <net/ncsi.h>
# include <net/net_namespace.h>
# include <net/sock.h>
# include "internal.h"
# include "ncsi-pkt.h"
u32 ncsi_calculate_checksum ( unsigned char * data , int len )
{
u32 checksum = 0 ;
int i ;
for ( i = 0 ; i < len ; i + = 2 )
checksum + = ( ( ( u32 ) data [ i ] < < 8 ) | data [ i + 1 ] ) ;
checksum = ( ~ checksum + 1 ) ;
return checksum ;
}
/* This function should be called after the data area has been
* populated completely .
*/
static void ncsi_cmd_build_header ( struct ncsi_pkt_hdr * h ,
struct ncsi_cmd_arg * nca )
{
u32 checksum ;
__be32 * pchecksum ;
h - > mc_id = 0 ;
h - > revision = NCSI_PKT_REVISION ;
h - > reserved = 0 ;
h - > id = nca - > id ;
h - > type = nca - > type ;
h - > channel = NCSI_TO_CHANNEL ( nca - > package ,
nca - > channel ) ;
h - > length = htons ( nca - > payload ) ;
h - > reserved1 [ 0 ] = 0 ;
h - > reserved1 [ 1 ] = 0 ;
/* Fill with calculated checksum */
checksum = ncsi_calculate_checksum ( ( unsigned char * ) h ,
sizeof ( * h ) + nca - > payload ) ;
pchecksum = ( __be32 * ) ( ( void * ) h + sizeof ( struct ncsi_pkt_hdr ) +
nca - > payload ) ;
* pchecksum = htonl ( checksum ) ;
}
static int ncsi_cmd_handler_default ( struct sk_buff * skb ,
struct ncsi_cmd_arg * nca )
{
struct ncsi_cmd_pkt * cmd ;
cmd = ( struct ncsi_cmd_pkt * ) skb_put ( skb , sizeof ( * cmd ) ) ;
memset ( cmd , 0 , sizeof ( * cmd ) ) ;
ncsi_cmd_build_header ( & cmd - > cmd . common , nca ) ;
return 0 ;
}
static int ncsi_cmd_handler_sp ( struct sk_buff * skb ,
struct ncsi_cmd_arg * nca )
{
struct ncsi_cmd_sp_pkt * cmd ;
cmd = ( struct ncsi_cmd_sp_pkt * ) skb_put ( skb , sizeof ( * cmd ) ) ;
memset ( cmd , 0 , sizeof ( * cmd ) ) ;
cmd - > hw_arbitration = nca - > bytes [ 0 ] ;
ncsi_cmd_build_header ( & cmd - > cmd . common , nca ) ;
return 0 ;
}
static int ncsi_cmd_handler_dc ( struct sk_buff * skb ,
struct ncsi_cmd_arg * nca )
{
struct ncsi_cmd_dc_pkt * cmd ;
cmd = ( struct ncsi_cmd_dc_pkt * ) skb_put ( skb , sizeof ( * cmd ) ) ;
memset ( cmd , 0 , sizeof ( * cmd ) ) ;
cmd - > ald = nca - > bytes [ 0 ] ;
ncsi_cmd_build_header ( & cmd - > cmd . common , nca ) ;
return 0 ;
}
static int ncsi_cmd_handler_rc ( struct sk_buff * skb ,
struct ncsi_cmd_arg * nca )
{
struct ncsi_cmd_rc_pkt * cmd ;
cmd = ( struct ncsi_cmd_rc_pkt * ) skb_put ( skb , sizeof ( * cmd ) ) ;
memset ( cmd , 0 , sizeof ( * cmd ) ) ;
ncsi_cmd_build_header ( & cmd - > cmd . common , nca ) ;
return 0 ;
}
static int ncsi_cmd_handler_ae ( struct sk_buff * skb ,
struct ncsi_cmd_arg * nca )
{
struct ncsi_cmd_ae_pkt * cmd ;
cmd = ( struct ncsi_cmd_ae_pkt * ) skb_put ( skb , sizeof ( * cmd ) ) ;
memset ( cmd , 0 , sizeof ( * cmd ) ) ;
cmd - > mc_id = nca - > bytes [ 0 ] ;
cmd - > mode = htonl ( nca - > dwords [ 1 ] ) ;
ncsi_cmd_build_header ( & cmd - > cmd . common , nca ) ;
return 0 ;
}
static int ncsi_cmd_handler_sl ( struct sk_buff * skb ,
struct ncsi_cmd_arg * nca )
{
struct ncsi_cmd_sl_pkt * cmd ;
cmd = ( struct ncsi_cmd_sl_pkt * ) skb_put ( skb , sizeof ( * cmd ) ) ;
memset ( cmd , 0 , sizeof ( * cmd ) ) ;
cmd - > mode = htonl ( nca - > dwords [ 0 ] ) ;
cmd - > oem_mode = htonl ( nca - > dwords [ 1 ] ) ;
ncsi_cmd_build_header ( & cmd - > cmd . common , nca ) ;
return 0 ;
}
static int ncsi_cmd_handler_svf ( struct sk_buff * skb ,
struct ncsi_cmd_arg * nca )
{
struct ncsi_cmd_svf_pkt * cmd ;
cmd = ( struct ncsi_cmd_svf_pkt * ) skb_put ( skb , sizeof ( * cmd ) ) ;
memset ( cmd , 0 , sizeof ( * cmd ) ) ;
cmd - > vlan = htons ( nca - > words [ 0 ] ) ;
cmd - > index = nca - > bytes [ 2 ] ;
cmd - > enable = nca - > bytes [ 3 ] ;
ncsi_cmd_build_header ( & cmd - > cmd . common , nca ) ;
return 0 ;
}
static int ncsi_cmd_handler_ev ( struct sk_buff * skb ,
struct ncsi_cmd_arg * nca )
{
struct ncsi_cmd_ev_pkt * cmd ;
cmd = ( struct ncsi_cmd_ev_pkt * ) skb_put ( skb , sizeof ( * cmd ) ) ;
memset ( cmd , 0 , sizeof ( * cmd ) ) ;
cmd - > mode = nca - > bytes [ 0 ] ;
ncsi_cmd_build_header ( & cmd - > cmd . common , nca ) ;
return 0 ;
}
static int ncsi_cmd_handler_sma ( struct sk_buff * skb ,
struct ncsi_cmd_arg * nca )
{
struct ncsi_cmd_sma_pkt * cmd ;
int i ;
cmd = ( struct ncsi_cmd_sma_pkt * ) skb_put ( skb , sizeof ( * cmd ) ) ;
memset ( cmd , 0 , sizeof ( * cmd ) ) ;
for ( i = 0 ; i < 6 ; i + + )
cmd - > mac [ i ] = nca - > bytes [ i ] ;
cmd - > index = nca - > bytes [ 6 ] ;
cmd - > at_e = nca - > bytes [ 7 ] ;
ncsi_cmd_build_header ( & cmd - > cmd . common , nca ) ;
return 0 ;
}
static int ncsi_cmd_handler_ebf ( struct sk_buff * skb ,
struct ncsi_cmd_arg * nca )
{
struct ncsi_cmd_ebf_pkt * cmd ;
cmd = ( struct ncsi_cmd_ebf_pkt * ) skb_put ( skb , sizeof ( * cmd ) ) ;
memset ( cmd , 0 , sizeof ( * cmd ) ) ;
cmd - > mode = htonl ( nca - > dwords [ 0 ] ) ;
ncsi_cmd_build_header ( & cmd - > cmd . common , nca ) ;
return 0 ;
}
static int ncsi_cmd_handler_egmf ( struct sk_buff * skb ,
struct ncsi_cmd_arg * nca )
{
struct ncsi_cmd_egmf_pkt * cmd ;
cmd = ( struct ncsi_cmd_egmf_pkt * ) skb_put ( skb , sizeof ( * cmd ) ) ;
memset ( cmd , 0 , sizeof ( * cmd ) ) ;
cmd - > mode = htonl ( nca - > dwords [ 0 ] ) ;
ncsi_cmd_build_header ( & cmd - > cmd . common , nca ) ;
return 0 ;
}
static int ncsi_cmd_handler_snfc ( struct sk_buff * skb ,
struct ncsi_cmd_arg * nca )
{
struct ncsi_cmd_snfc_pkt * cmd ;
cmd = ( struct ncsi_cmd_snfc_pkt * ) skb_put ( skb , sizeof ( * cmd ) ) ;
memset ( cmd , 0 , sizeof ( * cmd ) ) ;
cmd - > mode = nca - > bytes [ 0 ] ;
ncsi_cmd_build_header ( & cmd - > cmd . common , nca ) ;
return 0 ;
}
static struct ncsi_cmd_handler {
unsigned char type ;
int payload ;
int ( * handler ) ( struct sk_buff * skb ,
struct ncsi_cmd_arg * nca ) ;
} ncsi_cmd_handlers [ ] = {
{ NCSI_PKT_CMD_CIS , 0 , ncsi_cmd_handler_default } ,
{ NCSI_PKT_CMD_SP , 4 , ncsi_cmd_handler_sp } ,
{ NCSI_PKT_CMD_DP , 0 , ncsi_cmd_handler_default } ,
{ NCSI_PKT_CMD_EC , 0 , ncsi_cmd_handler_default } ,
{ NCSI_PKT_CMD_DC , 4 , ncsi_cmd_handler_dc } ,
{ NCSI_PKT_CMD_RC , 4 , ncsi_cmd_handler_rc } ,
{ NCSI_PKT_CMD_ECNT , 0 , ncsi_cmd_handler_default } ,
{ NCSI_PKT_CMD_DCNT , 0 , ncsi_cmd_handler_default } ,
{ NCSI_PKT_CMD_AE , 8 , ncsi_cmd_handler_ae } ,
{ NCSI_PKT_CMD_SL , 8 , ncsi_cmd_handler_sl } ,
{ NCSI_PKT_CMD_GLS , 0 , ncsi_cmd_handler_default } ,
{ NCSI_PKT_CMD_SVF , 4 , ncsi_cmd_handler_svf } ,
{ NCSI_PKT_CMD_EV , 4 , ncsi_cmd_handler_ev } ,
{ NCSI_PKT_CMD_DV , 0 , ncsi_cmd_handler_default } ,
{ NCSI_PKT_CMD_SMA , 8 , ncsi_cmd_handler_sma } ,
{ NCSI_PKT_CMD_EBF , 4 , ncsi_cmd_handler_ebf } ,
{ NCSI_PKT_CMD_DBF , 0 , ncsi_cmd_handler_default } ,
{ NCSI_PKT_CMD_EGMF , 4 , ncsi_cmd_handler_egmf } ,
{ NCSI_PKT_CMD_DGMF , 0 , ncsi_cmd_handler_default } ,
{ NCSI_PKT_CMD_SNFC , 4 , ncsi_cmd_handler_snfc } ,
{ NCSI_PKT_CMD_GVI , 0 , ncsi_cmd_handler_default } ,
{ NCSI_PKT_CMD_GC , 0 , ncsi_cmd_handler_default } ,
{ NCSI_PKT_CMD_GP , 0 , ncsi_cmd_handler_default } ,
{ NCSI_PKT_CMD_GCPS , 0 , ncsi_cmd_handler_default } ,
{ NCSI_PKT_CMD_GNS , 0 , ncsi_cmd_handler_default } ,
{ NCSI_PKT_CMD_GNPTS , 0 , ncsi_cmd_handler_default } ,
{ NCSI_PKT_CMD_GPS , 0 , ncsi_cmd_handler_default } ,
{ NCSI_PKT_CMD_OEM , 0 , NULL } ,
{ NCSI_PKT_CMD_PLDM , 0 , NULL } ,
{ NCSI_PKT_CMD_GPUUID , 0 , ncsi_cmd_handler_default }
} ;
static struct ncsi_request * ncsi_alloc_command ( struct ncsi_cmd_arg * nca )
{
struct ncsi_dev_priv * ndp = nca - > ndp ;
struct ncsi_dev * nd = & ndp - > ndev ;
struct net_device * dev = nd - > dev ;
int hlen = LL_RESERVED_SPACE ( dev ) ;
int tlen = dev - > needed_tailroom ;
int len = hlen + tlen ;
struct sk_buff * skb ;
struct ncsi_request * nr ;
2016-10-04 03:25:51 +03:00
nr = ncsi_alloc_request ( ndp , nca - > req_flags ) ;
2016-07-19 04:54:17 +03:00
if ( ! nr )
return NULL ;
/* NCSI command packet has 16-bytes header, payload, 4 bytes checksum.
* The packet needs padding if its payload is less than 26 bytes to
* meet 64 bytes minimal ethernet frame length .
*/
len + = sizeof ( struct ncsi_cmd_pkt_hdr ) + 4 ;
if ( nca - > payload < 26 )
len + = 26 ;
else
len + = nca - > payload ;
/* Allocate skb */
skb = alloc_skb ( len , GFP_ATOMIC ) ;
if ( ! skb ) {
ncsi_free_request ( nr ) ;
return NULL ;
}
nr - > cmd = skb ;
skb_reserve ( skb , hlen ) ;
skb_reset_network_header ( skb ) ;
skb - > dev = dev ;
skb - > protocol = htons ( ETH_P_NCSI ) ;
return nr ;
}
int ncsi_xmit_cmd ( struct ncsi_cmd_arg * nca )
{
struct ncsi_request * nr ;
struct ethhdr * eh ;
struct ncsi_cmd_handler * nch = NULL ;
int i , ret ;
/* Search for the handler */
for ( i = 0 ; i < ARRAY_SIZE ( ncsi_cmd_handlers ) ; i + + ) {
if ( ncsi_cmd_handlers [ i ] . type = = nca - > type ) {
if ( ncsi_cmd_handlers [ i ] . handler )
nch = & ncsi_cmd_handlers [ i ] ;
else
nch = NULL ;
break ;
}
}
if ( ! nch ) {
netdev_err ( nca - > ndp - > ndev . dev ,
" Cannot send packet with type 0x%02x \n " , nca - > type ) ;
return - ENOENT ;
}
/* Get packet payload length and allocate the request */
nca - > payload = nch - > payload ;
nr = ncsi_alloc_command ( nca ) ;
if ( ! nr )
return - ENOMEM ;
/* Prepare the packet */
nca - > id = nr - > id ;
ret = nch - > handler ( nr - > cmd , nca ) ;
if ( ret ) {
ncsi_free_request ( nr ) ;
return ret ;
}
/* Fill the ethernet header */
eh = ( struct ethhdr * ) skb_push ( nr - > cmd , sizeof ( * eh ) ) ;
eh - > h_proto = htons ( ETH_P_NCSI ) ;
eth_broadcast_addr ( eh - > h_dest ) ;
eth_broadcast_addr ( eh - > h_source ) ;
/* Start the timer for the request that might not have
* corresponding response . Given NCSI is an internal
* connection a 1 second delay should be sufficient .
*/
nr - > enabled = true ;
mod_timer ( & nr - > timer , jiffies + 1 * HZ ) ;
/* Send NCSI packet */
skb_get ( nr - > cmd ) ;
ret = dev_queue_xmit ( nr - > cmd ) ;
if ( ret < 0 ) {
ncsi_free_request ( nr ) ;
return ret ;
}
return 0 ;
}