ahci: routing policies based on device serial/model

Fixes #1882
This commit is contained in:
Igor Podkopaev 2016-02-11 22:03:38 +03:00 committed by Christian Helmuth
parent c17069b35e
commit b9263a7f4e
5 changed files with 103 additions and 23 deletions

View File

@ -18,10 +18,13 @@ which client can access a certain device:
! <any-service> <parent /> <any-child /> </any-service>
! </route>
! <config>
! <policy label="test-ahci" device="0" />
! <!-- use model and serial number -->
! <policy label="test-ahci" model="QEMU HARDDISK" serial="QM00005" />
! <!-- use controller port number -->
! <policy label="bench" device="1" />
! </config>
!</start>
In the example above, a session request labeled with "test-ahci"
gains access to device 0, while "bench" gains access to device 1.
In the example above, a session request labeled with "test-ahci" gains access to
a device with certain model and serial numbers, while "bench" gains access to
device at port 1.

View File

@ -185,6 +185,19 @@ struct Ahci
return port_num < MAX_PORTS && ports[port_num] && !port_claimed[port_num] &&
ports[port_num]->ready();
}
long device_number(const char *model_num, const char *serial_num)
{
for (long port_num = 0; port_num < MAX_PORTS; port_num++) {
Ata_driver* drv = dynamic_cast<Ata_driver *>(ports[port_num]);
if (!drv)
continue;
if (*drv->model == model_num && *drv->serial == serial_num)
return port_num;
}
return -1;
}
};
@ -220,4 +233,9 @@ bool Ahci_driver::is_avail(long device_num)
}
long Ahci_driver::device_number(char const *model_num, char const *serial_num)
{
return sata_ahci()->device_number(model_num, serial_num);
}

View File

@ -39,6 +39,7 @@ namespace Ahci_driver {
void init(Ahci_root &ep);
bool is_avail(long device_num);
long device_number(char const *model_num, char const *serial_num);
Block::Driver *claim_port(long device_num);
void free_port(long device_num);

View File

@ -26,6 +26,9 @@ struct Identity : Genode::Mmio
{
Identity(Genode::addr_t base) : Mmio(base) { }
struct Serial_number : Register_array<0x14, 8, 20, 8> { };
struct Model_number : Register_array<0x36, 8, 40, 8> { };
struct Queue_depth : Register<0x96, 16>
{
struct Max_depth : Bitfield<0, 5> { };
@ -71,6 +74,40 @@ struct Identity : Genode::Mmio
}
};
/**
* 16-bit word big endian device ASCII characters
*/
template <typename DEVICE_STRING>
struct String
{
char buf[DEVICE_STRING::ITEMS + 1];
String(Identity & info)
{
long j = 0;
for (unsigned long i = 0; i < DEVICE_STRING::ITEMS; i++) {
/* read and swap even and uneven characters */
char c = (char)info.read<DEVICE_STRING>(i ^ 1);
if (Genode::is_whitespace(c) && j == 0)
continue;
buf[j++] = c;
}
buf[j] = 0;
/* remove trailing white spaces */
while ((j > 0) && (buf[--j] == ' '))
buf[j] = 0;
}
bool operator == (char const *other) const
{
return strcmp(buf, other) == 0;
}
};
/**
* Commands to distinguish between ncq and non-ncq operation
*/
@ -135,9 +172,15 @@ struct Dma_ext_command : Io_command
*/
struct Ata_driver : Port_driver
{
Genode::Lazy_volatile_object<Identity> info;
Io_command *io_cmd = nullptr;
Block::Packet_descriptor pending[32];
typedef ::String<Identity::Serial_number> Serial_string;
typedef ::String<Identity::Model_number> Model_string;
Genode::Lazy_volatile_object<Identity> info;
Genode::Lazy_volatile_object<Serial_string> serial;
Genode::Lazy_volatile_object<Model_string> model;
Io_command *io_cmd = nullptr;
Block::Packet_descriptor pending[32];
Ata_driver(Port &port, Signal_context_capability state_change)
: Port_driver(port, state_change)
@ -242,8 +285,14 @@ struct Ata_driver : Port_driver
if (Port::Is::Dss::get(status) || Port::Is::Pss::get(status)) {
info.construct(device_info);
if (verbose)
serial.construct(*info);
model.construct(*info);
if (verbose) {
PLOG("\t\tmodel number: %s", model->buf);
PLOG("\t\tserial number: %s", serial->buf);
info->info();
}
check_device();
if (ncq_support())

View File

@ -61,27 +61,32 @@ class Block::Root_multiple_clients : public Root_component< ::Session_component>
Server::Entrypoint &_ep;
long _device_num(const char *session_label)
long _device_num(const char *session_label, char *model, char *sn, size_t bufs_len)
{
long num = -1;
try {
using namespace Genode;
Xml_node policy = config()->xml_node().sub_node("policy");
Xml_node policy = Genode::config()->xml_node().sub_node("policy");
for (;; policy = policy.next("policy")) {
char label_buf[64];
policy.attribute("label").value(label_buf, sizeof(label_buf));
for (;; policy = policy.next("policy")) {
char label_buf[64];
policy.attribute("label").value(label_buf, sizeof(label_buf));
if (Genode::strcmp(session_label, label_buf))
continue;
if (Genode::strcmp(session_label, label_buf))
continue;
/* read device attribute */
/* try read device port number attribute */
try {
policy.attribute("device").value(&num);
break;
}
} catch (...) {}
} catch (...) { }
/* try read device model and serial number attributes */
try {
model[0] = sn[0] = 0;
policy.attribute("model").value(model, bufs_len);
policy.attribute("serial").value(sn, bufs_len);
} catch (...) { }
break;
}
return num;
}
@ -96,12 +101,16 @@ class Block::Root_multiple_clients : public Root_component< ::Session_component>
/* TODO: build quota check */
/* Search for configured device */
char label_buf[64];
char label_buf[64], model_buf[64], sn_buf[64];
Genode::Arg_string::find_arg(args,
"label").string(label_buf,
sizeof(label_buf),
"<unlabeled>");
long num = _device_num(label_buf);
long num = _device_num(label_buf, model_buf, sn_buf, sizeof(model_buf));
/* prefer model/serial routing */
if ((model_buf[0] != 0) && (sn_buf[0] != 0))
num = Ahci_driver::device_number(model_buf, sn_buf);
if (num < 0) {
PERR("No confguration found for client: %s", label_buf);
throw Root::Invalid_args();