dynamic update of usb/pci/quirks tables
rizzo at iet.unipi.it
Sun Sep 28 10:19:28 UTC 2008
For some time I have been thinking of a way to solve the problem
in the subject., which is hitting me almost every time i try to
connect a newly bought device (scanner, phone, camera, mp3 player,
even some flash keys) to FreeBSD.
I was wondering what you people think about the following approach,
which can be implemented by a trivial userland program (kldpatch)
and probably be used with little effort or code bloat also in the
A common problem with newer USB (but also other) peripheral is that
often the kernel does not have the correct product and vendor
numbers in its internal tables used for device matching or
for applying quirks to certain devices.
The problem arises quite often with e.g. uscanner (where the matching
must be done through the table because there is no specific device
class), or with various umass devices (flash keys, cameras, phones,
etc.) which tend to require quirks because they do not implement
all the default features used by the umass driver.
The following approach can be used to patch the in-kernel structures
used to store the match/quirks tables:
+ use kldfind and kldsym to locate the module, address and size for
the desired data structure;
+ use kvm_open/kvm_read/kvm_write to read and possibly update the table.
Of course patching the live kernel is dangerous, so one way to make
the process a little less risky is to put some additional controls
in the frontend program which implements the above. In particular,
the frontend will contain a table of the form
module symbol record_size record_format...
which defines which modules/symbols can be patched and how each record
is supposed to be structured. As an example:
uscanner.ko uscanner_devs 8 2 2 4
umass.ko umass_devdescrs 16 4 4 4 2 2
if_sis.ko sis_devs 8 2 2 %s
tells that uscanner has 8-byte records made of 3 numeric fields
of size 2 2 4, umass.ko has 16-byte records with 5 numeric fields,
if_sis has 8-byte records whose last one is a string pointer,
(I could not find an instance with a fixed-size string).
The frontend will accept commands of the form
kldpatch if_sis.ko sis_devs write @3 0x1234 0x5678 "temporary entry"
which means 'patch record 3 with the values specified".
The frontend will make sure that operands are of the required size,
that the table entry is within the table, and that (in case of
patching constant strings) the replacement does not exceed the original
length of the string.
This approach does not address one case which is the "translation"
of one PCI/USB/bus id into another one to make a driver believe
that device X is instead device Y. It does address, however, the relatively
common case where recognised IDs are in an explicit device or quirks table.
I believe the same functionality can be added to the loader/kernel
itself, e.g. by telling /boot/loader to load a text file with all
the patches (i.e. the arguments to kldpatch) to be applied, and
hooking into the kernel so that right after boot, and right after
a module has been kldloaded, the patches are applied.
In terms of preventing the user from doing mistakes, there are a
few safety checks implemented by the internal table, which should at
least prevent the user from writing outside the allowed areas.
Clearly there is the issue of a possible mismatch between the
record structure as known to kldpatch, and the actual record structure
as defined in the .ko files -- but i don't know of a reasonably
simple method to extract the structure info from the C source and
pass them to kldpatch -- one way could be, perhaps, to add a special
symbol to the module itself so perhaps at least the structure info
can be declared within the module, and it is easier to be kept in sync.
More information about the freebsd-arch