/*
* 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/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"
static int ncsi_validate_rsp_pkt(struct ncsi_request *nr,
unsigned short payload)
{
struct ncsi_rsp_pkt_hdr *h;
u32 checksum;
__be32 *pchecksum;
/* Check NCSI packet header. We don't need validate
* the packet type, which should have been checked
* before calling this function.
*/
h = (struct ncsi_rsp_pkt_hdr *)skb_network_header(nr->rsp);
if (h->common.revision != NCSI_PKT_REVISION)
return -EINVAL;
if (ntohs(h->common.length) != payload)
return -EINVAL;
/* Check on code and reason */
if (ntohs(h->code) != NCSI_PKT_RSP_C_COMPLETED ||
ntohs(h->reason) != NCSI_PKT_RSP_R_NO_ERROR)
return -EINVAL;
/* Validate checksum, which might be zeroes if the
* sender doesn't support checksum according to NCSI
* specification.
*/
pchecksum = (__be32 *)((void *)(h + 1) + payload - 4);
if (ntohl(*pchecksum) == 0)
return 0;
checksum = ncsi_calculate_checksum((unsigned char *)h,
sizeof(*h) + payload - 4);
if (*pchecksum != htonl(checksum))
return -EINVAL;
return 0;
}
static int ncsi_rsp_handler_cis(struct ncsi_request *nr)
{
struct ncsi_rsp_pkt *rsp;
struct ncsi_dev_priv *ndp = nr->ndp;
struct ncsi_package *np;
struct ncsi_channel *nc;
unsigned char id;
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel, &np, &nc);
if (!nc) {
if (ndp->flags & NCSI_DEV_PROBED)
return -ENXIO;
id = NCSI_CHANNEL_INDEX(rsp->rsp.common.channel);
nc = ncsi_add_channel(np, id);
}
return nc ? 0 : -ENODEV;
}
static int ncsi_rsp_handler_sp(struct ncsi_request *nr)
{
struct ncsi_rsp_pkt *rsp;
struct ncsi_dev_priv *ndp = nr->ndp;
struct ncsi_package *np;
unsigned char id;
/* Add the package if it's not existing. Otherwise,
* to change the state of its child channels.
*/
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
&np, NULL);
if (!np) {
if (ndp->flags & NCSI_DEV_PROBED)
return -ENXIO;
id = NCSI_PACKAGE_INDEX(rsp->rsp.common.channel);
np = ncsi_add_package(ndp, id);
if (!np)
return -ENODEV;
}
return 0;
}
static int ncsi_rsp_handler_dp(struct ncsi_request *nr)
{
struct ncsi_rsp_pkt *rsp;
struct ncsi_dev_priv *ndp = nr->ndp;
struct ncsi_package *np;
struct ncsi_channel *nc;
unsigned long flags;
/* Find the package */
rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
&np, NULL);
if (!np)
return -ENODEV;
/* Change state of all channels attached to the package */
NCSI_FOR_EACH_CHANNEL(np, nc) {
spin_lock_irqsave(&nc->lock, flags