pci: deny access to registers used by pci driver

Fixes #1532
This commit is contained in:
Alexander Boettcher 2015-05-19 21:20:13 +02:00 committed by Christian Helmuth
parent cbc46a2276
commit 3a021c4c29
4 changed files with 88 additions and 22 deletions

View File

@ -67,6 +67,15 @@ namespace Pci {
(function << 8) | (function << 8) |
(addr & ~3) ); } (addr & ~3) ); }
Genode::Bit_array<256> _used;
void _use_register(unsigned char addr, unsigned short width)
{
for (unsigned i = 0; i < width; i++)
if (!_used.get(addr + i, 1))
_used.set(addr + i, 1);
}
public: public:
/** /**
@ -82,18 +91,32 @@ namespace Pci {
* *
* There is no range check for the input values. * There is no range check for the input values.
*/ */
unsigned read(int bus, int device, int function, int addr, unsigned read(int bus, int device, int function,
Device::Access_size size = Device::ACCESS_32BIT) unsigned char addr, Device::Access_size size,
bool track = true)
{ {
/* write target address */ /* write target address */
_io_port<REG_ADDR>()->outl(REG_ADDR, _cfg_addr(bus, device, function, addr)); _io_port<REG_ADDR>()->outl(REG_ADDR, _cfg_addr(bus, device, function, addr));
/* return read value */ /* return read value */
switch (size) { switch (size) {
case Device::ACCESS_8BIT: return _io_port<REG_DATA>()->inb(REG_DATA + (addr & 3)); case Device::ACCESS_8BIT:
case Device::ACCESS_16BIT: return _io_port<REG_DATA>()->inw(REG_DATA + (addr & 2)); if (track)
case Device::ACCESS_32BIT: return _io_port<REG_DATA>()->inl(REG_DATA); _use_register(addr, 1);
default: return ~0;
return _io_port<REG_DATA>()->inb(REG_DATA + (addr & 3));
case Device::ACCESS_16BIT:
if (track)
_use_register(addr, 2);
return _io_port<REG_DATA>()->inw(REG_DATA + (addr & 2));
case Device::ACCESS_32BIT:
if (track)
_use_register(addr, 4);
return _io_port<REG_DATA>()->inl(REG_DATA);
default:
return ~0U;
} }
} }
@ -109,25 +132,50 @@ namespace Pci {
* *
* There is no range check for the input values. * There is no range check for the input values.
*/ */
void write(int bus, int device, int function, int addr, unsigned value, void write(int bus, int device, int function, unsigned char addr,
Device::Access_size size = Device::ACCESS_32BIT) unsigned value, Device::Access_size size,
bool track = true)
{ {
/* write target address */ /* write target address */
_io_port<REG_ADDR>()->outl(REG_ADDR, _cfg_addr(bus, device, function, addr)); _io_port<REG_ADDR>()->outl(REG_ADDR, _cfg_addr(bus, device,
function, addr));
/* write value to targeted address */ /* write value to targeted address */
switch (size) { switch (size) {
case Device::ACCESS_8BIT: case Device::ACCESS_8BIT:
if (track)
_use_register(addr, 1);
_io_port<REG_DATA>()->outb(REG_DATA + (addr & 3), value); _io_port<REG_DATA>()->outb(REG_DATA + (addr & 3), value);
break; break;
case Device::ACCESS_16BIT: case Device::ACCESS_16BIT:
if (track)
_use_register(addr, 2);
_io_port<REG_DATA>()->outw(REG_DATA + (addr & 2), value); _io_port<REG_DATA>()->outw(REG_DATA + (addr & 2), value);
break; break;
case Device::ACCESS_32BIT: case Device::ACCESS_32BIT:
if (track)
_use_register(addr, 4);
_io_port<REG_DATA>()->outl(REG_DATA, value); _io_port<REG_DATA>()->outl(REG_DATA, value);
break; break;
} }
} }
bool reg_in_use(unsigned char addr, Device::Access_size size)
{
switch (size) {
case Device::ACCESS_8BIT:
return _used.get(addr, 1);
case Device::ACCESS_16BIT:
return _used.get(addr, 2);
case Device::ACCESS_32BIT:
return _used.get(addr, 4);
default:
return true;
}
}
}; };
} }

View File

@ -66,8 +66,16 @@ void Pci::Device_component::config_write(unsigned char address, unsigned value,
{ {
/* white list of ports which we permit to write */ /* white list of ports which we permit to write */
switch (address) { switch (address) {
case 0x40 ... 0xff: /* allow access to device-specific registers */ case 0x40 ... 0xff:
break; /* allow access to device-specific registers if not used by us */
if (!_device_config.reg_in_use(&_config_access, address, size))
break;
PERR("%x:%x:%x write access to address=%x value=0x%x"
" size=0x%x denied - it is used by the platform driver.",
_device_config.bus_number(), _device_config.device_number(),
_device_config.function_number(), address, value, size);
return;
case PCI_CMD_REG: /* COMMAND register - first byte */ case PCI_CMD_REG: /* COMMAND register - first byte */
if (size == Access_size::ACCESS_16BIT) if (size == Access_size::ACCESS_16BIT)
break; break;
@ -93,6 +101,7 @@ void Pci::Device_component::config_write(unsigned char address, unsigned value,
if (address == PCI_CMD_REG && value & PCI_CMD_DMA && _session) if (address == PCI_CMD_REG && value & PCI_CMD_DMA && _session)
_session->assign_device(this); _session->assign_device(this);
_device_config.write(&_config_access, address, value, size); _device_config.write(&_config_access, address, value, size,
_device_config.DONT_TRACK_ACCESS);
} }

View File

@ -318,7 +318,8 @@ class Pci::Device_component : public Genode::Rpc_object<Pci::Device>,
unsigned config_read(unsigned char address, Access_size size) override unsigned config_read(unsigned char address, Access_size size) override
{ {
return _device_config.read(&_config_access, address, size); return _device_config.read(&_config_access, address, size,
_device_config.DONT_TRACK_ACCESS);
} }
void config_write(unsigned char address, unsigned value, void config_write(unsigned char address, unsigned value,

View File

@ -78,7 +78,7 @@ namespace Pci {
return; return;
_device_id = pci_config->read(bus, device, function, 2, Device::ACCESS_16BIT); _device_id = pci_config->read(bus, device, function, 2, Device::ACCESS_16BIT);
_class_code = pci_config->read(bus, device, function, 8) >> 8; _class_code = pci_config->read(bus, device, function, 8, Device::ACCESS_32BIT) >> 8;
_class_code &= 0xffffff; _class_code &= 0xffffff;
_header_type = pci_config->read(bus, device, function, 0xe, Device::ACCESS_8BIT); _header_type = pci_config->read(bus, device, function, 0xe, Device::ACCESS_8BIT);
_header_type &= 0x7f; _header_type &= 0x7f;
@ -101,7 +101,7 @@ namespace Pci {
unsigned bar_idx = 0x10 + 4 * i; unsigned bar_idx = 0x10 + 4 * i;
/* read original base-address register value */ /* read original base-address register value */
unsigned orig_bar = pci_config->read(bus, device, function, bar_idx); unsigned orig_bar = pci_config->read(bus, device, function, bar_idx, Device::ACCESS_32BIT);
/* check for invalid resource */ /* check for invalid resource */
if (orig_bar == (unsigned)~0) { if (orig_bar == (unsigned)~0) {
@ -115,9 +115,9 @@ namespace Pci {
* of lowest-significant bits corresponding to the resource size. * of lowest-significant bits corresponding to the resource size.
* Finally, we write back the original value as assigned by the BIOS. * Finally, we write back the original value as assigned by the BIOS.
*/ */
pci_config->write(bus, device, function, bar_idx, ~0); pci_config->write(bus, device, function, bar_idx, ~0, Device::ACCESS_32BIT);
unsigned bar = pci_config->read(bus, device, function, bar_idx); unsigned bar = pci_config->read(bus, device, function, bar_idx, Device::ACCESS_32BIT);
pci_config->write(bus, device, function, bar_idx, orig_bar); pci_config->write(bus, device, function, bar_idx, orig_bar, Device::ACCESS_32BIT);
/* /*
* Scan base-address-register value for the lowest set bit but * Scan base-address-register value for the lowest set bit but
@ -172,20 +172,28 @@ namespace Pci {
/** /**
* Read configuration space * Read configuration space
*/ */
enum { DONT_TRACK_ACCESS = false };
unsigned read(Config_access *pci_config, unsigned char address, unsigned read(Config_access *pci_config, unsigned char address,
Device::Access_size size) Device::Access_size size, bool track = true)
{ {
return pci_config->read(_bus, _device, _function, address, size); return pci_config->read(_bus, _device, _function, address,
size, track);
} }
/** /**
* Write configuration space * Write configuration space
*/ */
void write(Config_access *pci_config, unsigned char address, void write(Config_access *pci_config, unsigned char address,
unsigned long value, Device::Access_size size) unsigned long value, Device::Access_size size,
bool track = true)
{ {
pci_config->write(_bus, _device, _function, address, value, size); pci_config->write(_bus, _device, _function, address, value,
size, track);
} }
bool reg_in_use(Config_access *pci_config, unsigned char address,
Device::Access_size size) {
return pci_config->reg_in_use(address, size); }
}; };
class Config_space : public Genode::List<Config_space>::Element class Config_space : public Genode::List<Config_space>::Element