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

r8169_firmware.c (4926B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/* r8169_firmware.c: RealTek 8169/8168/8101 ethernet driver.
      3 *
      4 * Copyright (c) 2002 ShuChen <shuchen@realtek.com.tw>
      5 * Copyright (c) 2003 - 2007 Francois Romieu <romieu@fr.zoreil.com>
      6 * Copyright (c) a lot of people too. Please respect their work.
      7 *
      8 * See MAINTAINERS file for support contact information.
      9 */
     10
     11#include <linux/delay.h>
     12#include <linux/firmware.h>
     13
     14#include "r8169_firmware.h"
     15
     16enum rtl_fw_opcode {
     17	PHY_READ		= 0x0,
     18	PHY_DATA_OR		= 0x1,
     19	PHY_DATA_AND		= 0x2,
     20	PHY_BJMPN		= 0x3,
     21	PHY_MDIO_CHG		= 0x4,
     22	PHY_CLEAR_READCOUNT	= 0x7,
     23	PHY_WRITE		= 0x8,
     24	PHY_READCOUNT_EQ_SKIP	= 0x9,
     25	PHY_COMP_EQ_SKIPN	= 0xa,
     26	PHY_COMP_NEQ_SKIPN	= 0xb,
     27	PHY_WRITE_PREVIOUS	= 0xc,
     28	PHY_SKIPN		= 0xd,
     29	PHY_DELAY_MS		= 0xe,
     30};
     31
     32struct fw_info {
     33	u32	magic;
     34	char	version[RTL_VER_SIZE];
     35	__le32	fw_start;
     36	__le32	fw_len;
     37	u8	chksum;
     38} __packed;
     39
     40#define FW_OPCODE_SIZE sizeof_field(struct rtl_fw_phy_action, code[0])
     41
     42static bool rtl_fw_format_ok(struct rtl_fw *rtl_fw)
     43{
     44	const struct firmware *fw = rtl_fw->fw;
     45	struct fw_info *fw_info = (struct fw_info *)fw->data;
     46	struct rtl_fw_phy_action *pa = &rtl_fw->phy_action;
     47
     48	if (fw->size < FW_OPCODE_SIZE)
     49		return false;
     50
     51	if (!fw_info->magic) {
     52		size_t i, size, start;
     53		u8 checksum = 0;
     54
     55		if (fw->size < sizeof(*fw_info))
     56			return false;
     57
     58		for (i = 0; i < fw->size; i++)
     59			checksum += fw->data[i];
     60		if (checksum != 0)
     61			return false;
     62
     63		start = le32_to_cpu(fw_info->fw_start);
     64		if (start > fw->size)
     65			return false;
     66
     67		size = le32_to_cpu(fw_info->fw_len);
     68		if (size > (fw->size - start) / FW_OPCODE_SIZE)
     69			return false;
     70
     71		strscpy(rtl_fw->version, fw_info->version, RTL_VER_SIZE);
     72
     73		pa->code = (__le32 *)(fw->data + start);
     74		pa->size = size;
     75	} else {
     76		if (fw->size % FW_OPCODE_SIZE)
     77			return false;
     78
     79		strscpy(rtl_fw->version, rtl_fw->fw_name, RTL_VER_SIZE);
     80
     81		pa->code = (__le32 *)fw->data;
     82		pa->size = fw->size / FW_OPCODE_SIZE;
     83	}
     84
     85	return true;
     86}
     87
     88static bool rtl_fw_data_ok(struct rtl_fw *rtl_fw)
     89{
     90	struct rtl_fw_phy_action *pa = &rtl_fw->phy_action;
     91	size_t index;
     92
     93	for (index = 0; index < pa->size; index++) {
     94		u32 action = le32_to_cpu(pa->code[index]);
     95		u32 val = action & 0x0000ffff;
     96		u32 regno = (action & 0x0fff0000) >> 16;
     97
     98		switch (action >> 28) {
     99		case PHY_READ:
    100		case PHY_DATA_OR:
    101		case PHY_DATA_AND:
    102		case PHY_CLEAR_READCOUNT:
    103		case PHY_WRITE:
    104		case PHY_WRITE_PREVIOUS:
    105		case PHY_DELAY_MS:
    106			break;
    107
    108		case PHY_MDIO_CHG:
    109			if (val > 1)
    110				goto out;
    111			break;
    112
    113		case PHY_BJMPN:
    114			if (regno > index)
    115				goto out;
    116			break;
    117		case PHY_READCOUNT_EQ_SKIP:
    118			if (index + 2 >= pa->size)
    119				goto out;
    120			break;
    121		case PHY_COMP_EQ_SKIPN:
    122		case PHY_COMP_NEQ_SKIPN:
    123		case PHY_SKIPN:
    124			if (index + 1 + regno >= pa->size)
    125				goto out;
    126			break;
    127
    128		default:
    129			dev_err(rtl_fw->dev, "Invalid action 0x%08x\n", action);
    130			return false;
    131		}
    132	}
    133
    134	return true;
    135out:
    136	dev_err(rtl_fw->dev, "Out of range of firmware\n");
    137	return false;
    138}
    139
    140void rtl_fw_write_firmware(struct rtl8169_private *tp, struct rtl_fw *rtl_fw)
    141{
    142	struct rtl_fw_phy_action *pa = &rtl_fw->phy_action;
    143	rtl_fw_write_t fw_write = rtl_fw->phy_write;
    144	rtl_fw_read_t fw_read = rtl_fw->phy_read;
    145	int predata = 0, count = 0;
    146	size_t index;
    147
    148	for (index = 0; index < pa->size; index++) {
    149		u32 action = le32_to_cpu(pa->code[index]);
    150		u32 data = action & 0x0000ffff;
    151		u32 regno = (action & 0x0fff0000) >> 16;
    152		enum rtl_fw_opcode opcode = action >> 28;
    153
    154		if (!action)
    155			break;
    156
    157		switch (opcode) {
    158		case PHY_READ:
    159			predata = fw_read(tp, regno);
    160			count++;
    161			break;
    162		case PHY_DATA_OR:
    163			predata |= data;
    164			break;
    165		case PHY_DATA_AND:
    166			predata &= data;
    167			break;
    168		case PHY_BJMPN:
    169			index -= (regno + 1);
    170			break;
    171		case PHY_MDIO_CHG:
    172			if (data) {
    173				fw_write = rtl_fw->mac_mcu_write;
    174				fw_read = rtl_fw->mac_mcu_read;
    175			} else {
    176				fw_write = rtl_fw->phy_write;
    177				fw_read = rtl_fw->phy_read;
    178			}
    179
    180			break;
    181		case PHY_CLEAR_READCOUNT:
    182			count = 0;
    183			break;
    184		case PHY_WRITE:
    185			fw_write(tp, regno, data);
    186			break;
    187		case PHY_READCOUNT_EQ_SKIP:
    188			if (count == data)
    189				index++;
    190			break;
    191		case PHY_COMP_EQ_SKIPN:
    192			if (predata == data)
    193				index += regno;
    194			break;
    195		case PHY_COMP_NEQ_SKIPN:
    196			if (predata != data)
    197				index += regno;
    198			break;
    199		case PHY_WRITE_PREVIOUS:
    200			fw_write(tp, regno, predata);
    201			break;
    202		case PHY_SKIPN:
    203			index += regno;
    204			break;
    205		case PHY_DELAY_MS:
    206			msleep(data);
    207			break;
    208		}
    209	}
    210}
    211
    212void rtl_fw_release_firmware(struct rtl_fw *rtl_fw)
    213{
    214	release_firmware(rtl_fw->fw);
    215}
    216
    217int rtl_fw_request_firmware(struct rtl_fw *rtl_fw)
    218{
    219	int rc;
    220
    221	rc = request_firmware(&rtl_fw->fw, rtl_fw->fw_name, rtl_fw->dev);
    222	if (rc < 0)
    223		goto out;
    224
    225	if (!rtl_fw_format_ok(rtl_fw) || !rtl_fw_data_ok(rtl_fw)) {
    226		release_firmware(rtl_fw->fw);
    227		rc = -EINVAL;
    228		goto out;
    229	}
    230
    231	return 0;
    232out:
    233	dev_err(rtl_fw->dev, "Unable to load firmware %s (%d)\n",
    234		rtl_fw->fw_name, rc);
    235	return rc;
    236}