cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

fweh.c (11482B)


      1// SPDX-License-Identifier: ISC
      2/*
      3 * Copyright (c) 2012 Broadcom Corporation
      4 */
      5#include <linux/netdevice.h>
      6
      7#include "brcmu_wifi.h"
      8#include "brcmu_utils.h"
      9
     10#include "cfg80211.h"
     11#include "core.h"
     12#include "debug.h"
     13#include "tracepoint.h"
     14#include "fweh.h"
     15#include "fwil.h"
     16#include "proto.h"
     17
     18/**
     19 * struct brcmf_fweh_queue_item - event item on event queue.
     20 *
     21 * @q: list element for queuing.
     22 * @code: event code.
     23 * @ifidx: interface index related to this event.
     24 * @ifaddr: ethernet address for interface.
     25 * @emsg: common parameters of the firmware event message.
     26 * @datalen: length of the data array
     27 * @data: event specific data part of the firmware event.
     28 */
     29struct brcmf_fweh_queue_item {
     30	struct list_head q;
     31	enum brcmf_fweh_event_code code;
     32	u8 ifidx;
     33	u8 ifaddr[ETH_ALEN];
     34	struct brcmf_event_msg_be emsg;
     35	u32 datalen;
     36	u8 data[];
     37};
     38
     39/*
     40 * struct brcmf_fweh_event_name - code, name mapping entry.
     41 */
     42struct brcmf_fweh_event_name {
     43	enum brcmf_fweh_event_code code;
     44	const char *name;
     45};
     46
     47#ifdef DEBUG
     48#define BRCMF_ENUM_DEF(id, val) \
     49	{ val, #id },
     50
     51/* array for mapping code to event name */
     52static struct brcmf_fweh_event_name fweh_event_names[] = {
     53	BRCMF_FWEH_EVENT_ENUM_DEFLIST
     54};
     55#undef BRCMF_ENUM_DEF
     56
     57/**
     58 * brcmf_fweh_event_name() - returns name for given event code.
     59 *
     60 * @code: code to lookup.
     61 */
     62const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code)
     63{
     64	int i;
     65	for (i = 0; i < ARRAY_SIZE(fweh_event_names); i++) {
     66		if (fweh_event_names[i].code == code)
     67			return fweh_event_names[i].name;
     68	}
     69	return "unknown";
     70}
     71#else
     72const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code)
     73{
     74	return "nodebug";
     75}
     76#endif
     77
     78/**
     79 * brcmf_fweh_queue_event() - create and queue event.
     80 *
     81 * @fweh: firmware event handling info.
     82 * @event: event queue entry.
     83 */
     84static void brcmf_fweh_queue_event(struct brcmf_fweh_info *fweh,
     85				   struct brcmf_fweh_queue_item *event)
     86{
     87	ulong flags;
     88
     89	spin_lock_irqsave(&fweh->evt_q_lock, flags);
     90	list_add_tail(&event->q, &fweh->event_q);
     91	spin_unlock_irqrestore(&fweh->evt_q_lock, flags);
     92	schedule_work(&fweh->event_work);
     93}
     94
     95static int brcmf_fweh_call_event_handler(struct brcmf_pub *drvr,
     96					 struct brcmf_if *ifp,
     97					 enum brcmf_fweh_event_code code,
     98					 struct brcmf_event_msg *emsg,
     99					 void *data)
    100{
    101	struct brcmf_fweh_info *fweh;
    102	int err = -EINVAL;
    103
    104	if (ifp) {
    105		fweh = &ifp->drvr->fweh;
    106
    107		/* handle the event if valid interface and handler */
    108		if (fweh->evt_handler[code])
    109			err = fweh->evt_handler[code](ifp, emsg, data);
    110		else
    111			bphy_err(drvr, "unhandled event %d ignored\n", code);
    112	} else {
    113		bphy_err(drvr, "no interface object\n");
    114	}
    115	return err;
    116}
    117
    118/**
    119 * brcmf_fweh_handle_if_event() - handle IF event.
    120 *
    121 * @drvr: driver information object.
    122 * @emsg: event message object.
    123 * @data: event object.
    124 */
    125static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr,
    126				       struct brcmf_event_msg *emsg,
    127				       void *data)
    128{
    129	struct brcmf_if_event *ifevent = data;
    130	struct brcmf_if *ifp;
    131	bool is_p2pdev;
    132
    133	brcmf_dbg(EVENT, "action: %u ifidx: %u bsscfgidx: %u flags: %u role: %u\n",
    134		  ifevent->action, ifevent->ifidx, ifevent->bsscfgidx,
    135		  ifevent->flags, ifevent->role);
    136
    137	/* The P2P Device interface event must not be ignored contrary to what
    138	 * firmware tells us. Older firmware uses p2p noif, with sta role.
    139	 * This should be accepted when p2pdev_setup is ongoing. TDLS setup will
    140	 * use the same ifevent and should be ignored.
    141	 */
    142	is_p2pdev = ((ifevent->flags & BRCMF_E_IF_FLAG_NOIF) &&
    143		     (ifevent->role == BRCMF_E_IF_ROLE_P2P_CLIENT ||
    144		      ((ifevent->role == BRCMF_E_IF_ROLE_STA) &&
    145		       (drvr->fweh.p2pdev_setup_ongoing))));
    146	if (!is_p2pdev && (ifevent->flags & BRCMF_E_IF_FLAG_NOIF)) {
    147		brcmf_dbg(EVENT, "event can be ignored\n");
    148		return;
    149	}
    150	if (ifevent->ifidx >= BRCMF_MAX_IFS) {
    151		bphy_err(drvr, "invalid interface index: %u\n", ifevent->ifidx);
    152		return;
    153	}
    154
    155	ifp = drvr->iflist[ifevent->bsscfgidx];
    156
    157	if (ifevent->action == BRCMF_E_IF_ADD) {
    158		brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname,
    159			  emsg->addr);
    160		ifp = brcmf_add_if(drvr, ifevent->bsscfgidx, ifevent->ifidx,
    161				   is_p2pdev, emsg->ifname, emsg->addr);
    162		if (IS_ERR(ifp))
    163			return;
    164		if (!is_p2pdev)
    165			brcmf_proto_add_if(drvr, ifp);
    166		if (!drvr->fweh.evt_handler[BRCMF_E_IF])
    167			if (brcmf_net_attach(ifp, false) < 0)
    168				return;
    169	}
    170
    171	if (ifp && ifevent->action == BRCMF_E_IF_CHANGE)
    172		brcmf_proto_reset_if(drvr, ifp);
    173
    174	brcmf_fweh_call_event_handler(drvr, ifp, emsg->event_code, emsg,
    175				      data);
    176
    177	if (ifp && ifevent->action == BRCMF_E_IF_DEL) {
    178		bool armed = brcmf_cfg80211_vif_event_armed(drvr->config);
    179
    180		/* Default handling in case no-one waits for this event */
    181		if (!armed)
    182			brcmf_remove_interface(ifp, false);
    183	}
    184}
    185
    186/**
    187 * brcmf_fweh_dequeue_event() - get event from the queue.
    188 *
    189 * @fweh: firmware event handling info.
    190 */
    191static struct brcmf_fweh_queue_item *
    192brcmf_fweh_dequeue_event(struct brcmf_fweh_info *fweh)
    193{
    194	struct brcmf_fweh_queue_item *event = NULL;
    195	ulong flags;
    196
    197	spin_lock_irqsave(&fweh->evt_q_lock, flags);
    198	if (!list_empty(&fweh->event_q)) {
    199		event = list_first_entry(&fweh->event_q,
    200					 struct brcmf_fweh_queue_item, q);
    201		list_del(&event->q);
    202	}
    203	spin_unlock_irqrestore(&fweh->evt_q_lock, flags);
    204
    205	return event;
    206}
    207
    208/**
    209 * brcmf_fweh_event_worker() - firmware event worker.
    210 *
    211 * @work: worker object.
    212 */
    213static void brcmf_fweh_event_worker(struct work_struct *work)
    214{
    215	struct brcmf_pub *drvr;
    216	struct brcmf_if *ifp;
    217	struct brcmf_fweh_info *fweh;
    218	struct brcmf_fweh_queue_item *event;
    219	int err = 0;
    220	struct brcmf_event_msg_be *emsg_be;
    221	struct brcmf_event_msg emsg;
    222
    223	fweh = container_of(work, struct brcmf_fweh_info, event_work);
    224	drvr = container_of(fweh, struct brcmf_pub, fweh);
    225
    226	while ((event = brcmf_fweh_dequeue_event(fweh))) {
    227		brcmf_dbg(EVENT, "event %s (%u) ifidx %u bsscfg %u addr %pM\n",
    228			  brcmf_fweh_event_name(event->code), event->code,
    229			  event->emsg.ifidx, event->emsg.bsscfgidx,
    230			  event->emsg.addr);
    231
    232		/* convert event message */
    233		emsg_be = &event->emsg;
    234		emsg.version = be16_to_cpu(emsg_be->version);
    235		emsg.flags = be16_to_cpu(emsg_be->flags);
    236		emsg.event_code = event->code;
    237		emsg.status = be32_to_cpu(emsg_be->status);
    238		emsg.reason = be32_to_cpu(emsg_be->reason);
    239		emsg.auth_type = be32_to_cpu(emsg_be->auth_type);
    240		emsg.datalen = be32_to_cpu(emsg_be->datalen);
    241		memcpy(emsg.addr, emsg_be->addr, ETH_ALEN);
    242		memcpy(emsg.ifname, emsg_be->ifname, sizeof(emsg.ifname));
    243		emsg.ifidx = emsg_be->ifidx;
    244		emsg.bsscfgidx = emsg_be->bsscfgidx;
    245
    246		brcmf_dbg(EVENT, "  version %u flags %u status %u reason %u\n",
    247			  emsg.version, emsg.flags, emsg.status, emsg.reason);
    248		brcmf_dbg_hex_dump(BRCMF_EVENT_ON(), event->data,
    249				   min_t(u32, emsg.datalen, 64),
    250				   "event payload, len=%d\n", emsg.datalen);
    251
    252		/* special handling of interface event */
    253		if (event->code == BRCMF_E_IF) {
    254			brcmf_fweh_handle_if_event(drvr, &emsg, event->data);
    255			goto event_free;
    256		}
    257
    258		if (event->code == BRCMF_E_TDLS_PEER_EVENT)
    259			ifp = drvr->iflist[0];
    260		else
    261			ifp = drvr->iflist[emsg.bsscfgidx];
    262		err = brcmf_fweh_call_event_handler(drvr, ifp, event->code,
    263						    &emsg, event->data);
    264		if (err) {
    265			bphy_err(drvr, "event handler failed (%d)\n",
    266				 event->code);
    267			err = 0;
    268		}
    269event_free:
    270		kfree(event);
    271	}
    272}
    273
    274/**
    275 * brcmf_fweh_p2pdev_setup() - P2P device setup ongoing (or not).
    276 *
    277 * @ifp: ifp on which setup is taking place or finished.
    278 * @ongoing: p2p device setup in progress (or not).
    279 */
    280void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing)
    281{
    282	ifp->drvr->fweh.p2pdev_setup_ongoing = ongoing;
    283}
    284
    285/**
    286 * brcmf_fweh_attach() - initialize firmware event handling.
    287 *
    288 * @drvr: driver information object.
    289 */
    290void brcmf_fweh_attach(struct brcmf_pub *drvr)
    291{
    292	struct brcmf_fweh_info *fweh = &drvr->fweh;
    293	INIT_WORK(&fweh->event_work, brcmf_fweh_event_worker);
    294	spin_lock_init(&fweh->evt_q_lock);
    295	INIT_LIST_HEAD(&fweh->event_q);
    296}
    297
    298/**
    299 * brcmf_fweh_detach() - cleanup firmware event handling.
    300 *
    301 * @drvr: driver information object.
    302 */
    303void brcmf_fweh_detach(struct brcmf_pub *drvr)
    304{
    305	struct brcmf_fweh_info *fweh = &drvr->fweh;
    306
    307	/* cancel the worker if initialized */
    308	if (fweh->event_work.func) {
    309		cancel_work_sync(&fweh->event_work);
    310		WARN_ON(!list_empty(&fweh->event_q));
    311		memset(fweh->evt_handler, 0, sizeof(fweh->evt_handler));
    312	}
    313}
    314
    315/**
    316 * brcmf_fweh_register() - register handler for given event code.
    317 *
    318 * @drvr: driver information object.
    319 * @code: event code.
    320 * @handler: handler for the given event code.
    321 */
    322int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code,
    323			brcmf_fweh_handler_t handler)
    324{
    325	if (drvr->fweh.evt_handler[code]) {
    326		bphy_err(drvr, "event code %d already registered\n", code);
    327		return -ENOSPC;
    328	}
    329	drvr->fweh.evt_handler[code] = handler;
    330	brcmf_dbg(TRACE, "event handler registered for %s\n",
    331		  brcmf_fweh_event_name(code));
    332	return 0;
    333}
    334
    335/**
    336 * brcmf_fweh_unregister() - remove handler for given code.
    337 *
    338 * @drvr: driver information object.
    339 * @code: event code.
    340 */
    341void brcmf_fweh_unregister(struct brcmf_pub *drvr,
    342			   enum brcmf_fweh_event_code code)
    343{
    344	brcmf_dbg(TRACE, "event handler cleared for %s\n",
    345		  brcmf_fweh_event_name(code));
    346	drvr->fweh.evt_handler[code] = NULL;
    347}
    348
    349/**
    350 * brcmf_fweh_activate_events() - enables firmware events registered.
    351 *
    352 * @ifp: primary interface object.
    353 */
    354int brcmf_fweh_activate_events(struct brcmf_if *ifp)
    355{
    356	struct brcmf_pub *drvr = ifp->drvr;
    357	int i, err;
    358	s8 eventmask[BRCMF_EVENTING_MASK_LEN];
    359
    360	memset(eventmask, 0, sizeof(eventmask));
    361	for (i = 0; i < BRCMF_E_LAST; i++) {
    362		if (ifp->drvr->fweh.evt_handler[i]) {
    363			brcmf_dbg(EVENT, "enable event %s\n",
    364				  brcmf_fweh_event_name(i));
    365			setbit(eventmask, i);
    366		}
    367	}
    368
    369	/* want to handle IF event as well */
    370	brcmf_dbg(EVENT, "enable event IF\n");
    371	setbit(eventmask, BRCMF_E_IF);
    372
    373	err = brcmf_fil_iovar_data_set(ifp, "event_msgs",
    374				       eventmask, BRCMF_EVENTING_MASK_LEN);
    375	if (err)
    376		bphy_err(drvr, "Set event_msgs error (%d)\n", err);
    377
    378	return err;
    379}
    380
    381/**
    382 * brcmf_fweh_process_event() - process skb as firmware event.
    383 *
    384 * @drvr: driver information object.
    385 * @event_packet: event packet to process.
    386 * @packet_len: length of the packet
    387 * @gfp: memory allocation flags.
    388 *
    389 * If the packet buffer contains a firmware event message it will
    390 * dispatch the event to a registered handler (using worker).
    391 */
    392void brcmf_fweh_process_event(struct brcmf_pub *drvr,
    393			      struct brcmf_event *event_packet,
    394			      u32 packet_len, gfp_t gfp)
    395{
    396	enum brcmf_fweh_event_code code;
    397	struct brcmf_fweh_info *fweh = &drvr->fweh;
    398	struct brcmf_fweh_queue_item *event;
    399	void *data;
    400	u32 datalen;
    401
    402	/* get event info */
    403	code = get_unaligned_be32(&event_packet->msg.event_type);
    404	datalen = get_unaligned_be32(&event_packet->msg.datalen);
    405	data = &event_packet[1];
    406
    407	if (code >= BRCMF_E_LAST)
    408		return;
    409
    410	if (code != BRCMF_E_IF && !fweh->evt_handler[code])
    411		return;
    412
    413	if (datalen > BRCMF_DCMD_MAXLEN ||
    414	    datalen + sizeof(*event_packet) > packet_len)
    415		return;
    416
    417	event = kzalloc(sizeof(*event) + datalen, gfp);
    418	if (!event)
    419		return;
    420
    421	event->code = code;
    422	event->ifidx = event_packet->msg.ifidx;
    423
    424	/* use memcpy to get aligned event message */
    425	memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg));
    426	memcpy(event->data, data, datalen);
    427	event->datalen = datalen;
    428	memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN);
    429
    430	brcmf_fweh_queue_event(fweh, event);
    431}