e1000e: write protect ICHx NVM to prevent malicious write/erase
Set the hardware to ignore all write/erase cycles to the GbE region in the ICHx NVM. This feature can be disabled by the WriteProtectNVM module parameter (enabled by default) only after a hardware reset, but the machine must be power cycled before trying to enable writes. Signed-off-by: Bruce Allan <bruce.w.allan@intel.com> Signed-off-by: Jesse Brandeburg <jesse.brandeburg@intel.com> CC: arjan@linux.intel.com Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
committed by
Linus Torvalds
parent
20b918dc77
commit
4a77035828
@@ -305,6 +305,7 @@ struct e1000_info {
|
|||||||
#define FLAG_HAS_CTRLEXT_ON_LOAD (1 << 5)
|
#define FLAG_HAS_CTRLEXT_ON_LOAD (1 << 5)
|
||||||
#define FLAG_HAS_SWSM_ON_LOAD (1 << 6)
|
#define FLAG_HAS_SWSM_ON_LOAD (1 << 6)
|
||||||
#define FLAG_HAS_JUMBO_FRAMES (1 << 7)
|
#define FLAG_HAS_JUMBO_FRAMES (1 << 7)
|
||||||
|
#define FLAG_READ_ONLY_NVM (1 << 8)
|
||||||
#define FLAG_IS_ICH (1 << 9)
|
#define FLAG_IS_ICH (1 << 9)
|
||||||
#define FLAG_HAS_SMART_POWER_DOWN (1 << 11)
|
#define FLAG_HAS_SMART_POWER_DOWN (1 << 11)
|
||||||
#define FLAG_IS_QUAD_PORT_A (1 << 12)
|
#define FLAG_IS_QUAD_PORT_A (1 << 12)
|
||||||
@@ -385,6 +386,7 @@ extern bool e1000e_enable_mng_pass_thru(struct e1000_hw *hw);
|
|||||||
extern bool e1000e_get_laa_state_82571(struct e1000_hw *hw);
|
extern bool e1000e_get_laa_state_82571(struct e1000_hw *hw);
|
||||||
extern void e1000e_set_laa_state_82571(struct e1000_hw *hw, bool state);
|
extern void e1000e_set_laa_state_82571(struct e1000_hw *hw, bool state);
|
||||||
|
|
||||||
|
extern void e1000e_write_protect_nvm_ich8lan(struct e1000_hw *hw);
|
||||||
extern void e1000e_set_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw,
|
extern void e1000e_set_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw,
|
||||||
bool state);
|
bool state);
|
||||||
extern void e1000e_igp3_phy_powerdown_workaround_ich8lan(struct e1000_hw *hw);
|
extern void e1000e_igp3_phy_powerdown_workaround_ich8lan(struct e1000_hw *hw);
|
||||||
|
@@ -529,6 +529,9 @@ static int e1000_set_eeprom(struct net_device *netdev,
|
|||||||
if (eeprom->magic != (adapter->pdev->vendor | (adapter->pdev->device << 16)))
|
if (eeprom->magic != (adapter->pdev->vendor | (adapter->pdev->device << 16)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
|
if (adapter->flags & FLAG_READ_ONLY_NVM)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
max_len = hw->nvm.word_size * 2;
|
max_len = hw->nvm.word_size * 2;
|
||||||
|
|
||||||
first_word = eeprom->offset >> 1;
|
first_word = eeprom->offset >> 1;
|
||||||
|
@@ -58,6 +58,7 @@
|
|||||||
#define ICH_FLASH_HSFCTL 0x0006
|
#define ICH_FLASH_HSFCTL 0x0006
|
||||||
#define ICH_FLASH_FADDR 0x0008
|
#define ICH_FLASH_FADDR 0x0008
|
||||||
#define ICH_FLASH_FDATA0 0x0010
|
#define ICH_FLASH_FDATA0 0x0010
|
||||||
|
#define ICH_FLASH_PR0 0x0074
|
||||||
|
|
||||||
#define ICH_FLASH_READ_COMMAND_TIMEOUT 500
|
#define ICH_FLASH_READ_COMMAND_TIMEOUT 500
|
||||||
#define ICH_FLASH_WRITE_COMMAND_TIMEOUT 500
|
#define ICH_FLASH_WRITE_COMMAND_TIMEOUT 500
|
||||||
@@ -150,6 +151,19 @@ union ich8_hws_flash_regacc {
|
|||||||
u16 regval;
|
u16 regval;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* ICH Flash Protected Region */
|
||||||
|
union ich8_flash_protected_range {
|
||||||
|
struct ich8_pr {
|
||||||
|
u32 base:13; /* 0:12 Protected Range Base */
|
||||||
|
u32 reserved1:2; /* 13:14 Reserved */
|
||||||
|
u32 rpe:1; /* 15 Read Protection Enable */
|
||||||
|
u32 limit:13; /* 16:28 Protected Range Limit */
|
||||||
|
u32 reserved2:2; /* 29:30 Reserved */
|
||||||
|
u32 wpe:1; /* 31 Write Protection Enable */
|
||||||
|
} range;
|
||||||
|
u32 regval;
|
||||||
|
};
|
||||||
|
|
||||||
static s32 e1000_setup_link_ich8lan(struct e1000_hw *hw);
|
static s32 e1000_setup_link_ich8lan(struct e1000_hw *hw);
|
||||||
static void e1000_clear_hw_cntrs_ich8lan(struct e1000_hw *hw);
|
static void e1000_clear_hw_cntrs_ich8lan(struct e1000_hw *hw);
|
||||||
static void e1000_initialize_hw_bits_ich8lan(struct e1000_hw *hw);
|
static void e1000_initialize_hw_bits_ich8lan(struct e1000_hw *hw);
|
||||||
@@ -1284,6 +1298,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw)
|
|||||||
* programming failed.
|
* programming failed.
|
||||||
*/
|
*/
|
||||||
if (ret_val) {
|
if (ret_val) {
|
||||||
|
/* Possibly read-only, see e1000e_write_protect_nvm_ich8lan() */
|
||||||
hw_dbg(hw, "Flash commit failed.\n");
|
hw_dbg(hw, "Flash commit failed.\n");
|
||||||
e1000_release_swflag_ich8lan(hw);
|
e1000_release_swflag_ich8lan(hw);
|
||||||
return ret_val;
|
return ret_val;
|
||||||
@@ -1373,6 +1388,49 @@ static s32 e1000_validate_nvm_checksum_ich8lan(struct e1000_hw *hw)
|
|||||||
return e1000e_validate_nvm_checksum_generic(hw);
|
return e1000e_validate_nvm_checksum_generic(hw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* e1000e_write_protect_nvm_ich8lan - Make the NVM read-only
|
||||||
|
* @hw: pointer to the HW structure
|
||||||
|
*
|
||||||
|
* To prevent malicious write/erase of the NVM, set it to be read-only
|
||||||
|
* so that the hardware ignores all write/erase cycles of the NVM via
|
||||||
|
* the flash control registers. The shadow-ram copy of the NVM will
|
||||||
|
* still be updated, however any updates to this copy will not stick
|
||||||
|
* across driver reloads.
|
||||||
|
**/
|
||||||
|
void e1000e_write_protect_nvm_ich8lan(struct e1000_hw *hw)
|
||||||
|
{
|
||||||
|
union ich8_flash_protected_range pr0;
|
||||||
|
union ich8_hws_flash_status hsfsts;
|
||||||
|
u32 gfpreg;
|
||||||
|
s32 ret_val;
|
||||||
|
|
||||||
|
ret_val = e1000_acquire_swflag_ich8lan(hw);
|
||||||
|
if (ret_val)
|
||||||
|
return;
|
||||||
|
|
||||||
|
gfpreg = er32flash(ICH_FLASH_GFPREG);
|
||||||
|
|
||||||
|
/* Write-protect GbE Sector of NVM */
|
||||||
|
pr0.regval = er32flash(ICH_FLASH_PR0);
|
||||||
|
pr0.range.base = gfpreg & FLASH_GFPREG_BASE_MASK;
|
||||||
|
pr0.range.limit = ((gfpreg >> 16) & FLASH_GFPREG_BASE_MASK);
|
||||||
|
pr0.range.wpe = true;
|
||||||
|
ew32flash(ICH_FLASH_PR0, pr0.regval);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lock down a subset of GbE Flash Control Registers, e.g.
|
||||||
|
* PR0 to prevent the write-protection from being lifted.
|
||||||
|
* Once FLOCKDN is set, the registers protected by it cannot
|
||||||
|
* be written until FLOCKDN is cleared by a hardware reset.
|
||||||
|
*/
|
||||||
|
hsfsts.regval = er16flash(ICH_FLASH_HSFSTS);
|
||||||
|
hsfsts.hsf_status.flockdn = true;
|
||||||
|
ew32flash(ICH_FLASH_HSFSTS, hsfsts.regval);
|
||||||
|
|
||||||
|
e1000_release_swflag_ich8lan(hw);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* e1000_write_flash_data_ich8lan - Writes bytes to the NVM
|
* e1000_write_flash_data_ich8lan - Writes bytes to the NVM
|
||||||
* @hw: pointer to the HW structure
|
* @hw: pointer to the HW structure
|
||||||
|
@@ -47,7 +47,7 @@
|
|||||||
|
|
||||||
#include "e1000.h"
|
#include "e1000.h"
|
||||||
|
|
||||||
#define DRV_VERSION "0.3.3.3-k2"
|
#define DRV_VERSION "0.3.3.3-k4"
|
||||||
char e1000e_driver_name[] = "e1000e";
|
char e1000e_driver_name[] = "e1000e";
|
||||||
const char e1000e_driver_version[] = DRV_VERSION;
|
const char e1000e_driver_version[] = DRV_VERSION;
|
||||||
|
|
||||||
@@ -4467,6 +4467,8 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
|
|||||||
|
|
||||||
adapter->bd_number = cards_found++;
|
adapter->bd_number = cards_found++;
|
||||||
|
|
||||||
|
e1000e_check_options(adapter);
|
||||||
|
|
||||||
/* setup adapter struct */
|
/* setup adapter struct */
|
||||||
err = e1000_sw_init(adapter);
|
err = e1000_sw_init(adapter);
|
||||||
if (err)
|
if (err)
|
||||||
@@ -4482,6 +4484,10 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
|
|||||||
if (err)
|
if (err)
|
||||||
goto err_hw_init;
|
goto err_hw_init;
|
||||||
|
|
||||||
|
if ((adapter->flags & FLAG_IS_ICH) &&
|
||||||
|
(adapter->flags & FLAG_READ_ONLY_NVM))
|
||||||
|
e1000e_write_protect_nvm_ich8lan(&adapter->hw);
|
||||||
|
|
||||||
hw->mac.ops.get_bus_info(&adapter->hw);
|
hw->mac.ops.get_bus_info(&adapter->hw);
|
||||||
|
|
||||||
adapter->hw.phy.autoneg_wait_to_complete = 0;
|
adapter->hw.phy.autoneg_wait_to_complete = 0;
|
||||||
@@ -4573,8 +4579,6 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
|
|||||||
INIT_WORK(&adapter->reset_task, e1000_reset_task);
|
INIT_WORK(&adapter->reset_task, e1000_reset_task);
|
||||||
INIT_WORK(&adapter->watchdog_task, e1000_watchdog_task);
|
INIT_WORK(&adapter->watchdog_task, e1000_watchdog_task);
|
||||||
|
|
||||||
e1000e_check_options(adapter);
|
|
||||||
|
|
||||||
/* Initialize link parameters. User can change them with ethtool */
|
/* Initialize link parameters. User can change them with ethtool */
|
||||||
adapter->hw.mac.autoneg = 1;
|
adapter->hw.mac.autoneg = 1;
|
||||||
adapter->fc_autoneg = 1;
|
adapter->fc_autoneg = 1;
|
||||||
|
@@ -133,6 +133,15 @@ E1000_PARAM(SmartPowerDownEnable, "Enable PHY smart power down");
|
|||||||
*/
|
*/
|
||||||
E1000_PARAM(KumeranLockLoss, "Enable Kumeran lock loss workaround");
|
E1000_PARAM(KumeranLockLoss, "Enable Kumeran lock loss workaround");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write Protect NVM
|
||||||
|
*
|
||||||
|
* Valid Range: 0, 1
|
||||||
|
*
|
||||||
|
* Default Value: 1 (enabled)
|
||||||
|
*/
|
||||||
|
E1000_PARAM(WriteProtectNVM, "Write-protect NVM [WARNING: disabling this can lead to corrupted NVM]");
|
||||||
|
|
||||||
struct e1000_option {
|
struct e1000_option {
|
||||||
enum { enable_option, range_option, list_option } type;
|
enum { enable_option, range_option, list_option } type;
|
||||||
const char *name;
|
const char *name;
|
||||||
@@ -388,4 +397,25 @@ void __devinit e1000e_check_options(struct e1000_adapter *adapter)
|
|||||||
opt.def);
|
opt.def);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{ /* Write-protect NVM */
|
||||||
|
const struct e1000_option opt = {
|
||||||
|
.type = enable_option,
|
||||||
|
.name = "Write-protect NVM",
|
||||||
|
.err = "defaulting to Enabled",
|
||||||
|
.def = OPTION_ENABLED
|
||||||
|
};
|
||||||
|
|
||||||
|
if (adapter->flags & FLAG_IS_ICH) {
|
||||||
|
if (num_WriteProtectNVM > bd) {
|
||||||
|
unsigned int write_protect_nvm = WriteProtectNVM[bd];
|
||||||
|
e1000_validate_option(&write_protect_nvm, &opt,
|
||||||
|
adapter);
|
||||||
|
if (write_protect_nvm)
|
||||||
|
adapter->flags |= FLAG_READ_ONLY_NVM;
|
||||||
|
} else {
|
||||||
|
if (opt.def)
|
||||||
|
adapter->flags |= FLAG_READ_ONLY_NVM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user