Commit 4a770358 authored by Bruce Allan's avatar Bruce Allan Committed by Linus Torvalds

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: default avatarBruce Allan <bruce.w.allan@intel.com>
Signed-off-by: default avatarJesse Brandeburg <jesse.brandeburg@intel.com>
CC: arjan@linux.intel.com
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 20b918dc
...@@ -305,6 +305,7 @@ struct e1000_info { ...@@ -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); ...@@ -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, ...@@ -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 @@ ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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"); ...@@ -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) ...@@ -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;
}
}
}
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment