OpenSolaris

  subsites   code review   repo   packages   bugs   defect   polls   planet

HAL reverse engineered

Artem Kachitchkine <artem.kachitchkin@sun.com>
Last updated: 4/3/2006

Table of Contents

  1. 1. Introduction
  2. 2. Architecture overview
  3. 3. Properties
  4. 4. Installed components
  5. 5. HAL interfaces
  6. 6. Source structure
  7. 7. Hotplug and coldplug
  8. 8. Sources of device information
    1. 8.1 sysfs
    2. 8.2 Probing
  9. 9. Addons and callouts

1. Introduction

In the beginning I spent a fair amount of time reverse engineering HAL, as many of the implementation details are not in the spec (nor should they be), but as an engineer I wanted to know more. I captured my initial findings here, and though a bit dated, it can save time to the HAL newbies out there. This document can be used as a supplement to the HAL specification. There's a bit of Solaris bias in the article. Feel free to send be updates or requests for enhancements.

2. Architecture overview

Removable media managers are essentially specialized automounters. Vold plays this role in Solaris, ready to pass the reign to gnome-volume-manager and friends. It is important to understand that HAL itself is not a media manager: it does not mount anything, it only provides an abstration that allows to write the actual managers in a portable way.

HAL specification includes this short and rather precise summary:

HAL (Hardware Abstraction Layer) provides a view of the various hardware attached to a system [updated dynamically as hardware configuration changes, via hotplug or other mechanisms]. HAL represents a piece of hardware as a device object. A device object is identified by a unique identifer and carries a set of key/value pairs referred to as device properties. Some properties are derived from the actual hardware, some are merged from device information files (.fdi files) and some are related to the actual device configuration.

HAL provides an easy-to-use API through D-BUS which is an IPC framework that, among other things, provides a system-wide message-bus that allows applications to talk to one another. Specifically, D-BUS provides asynchronous notification such that HAL can notify other peers on the message-bus when devices are added and removed as well as when properties on a device are changing.

The most important goal of HAL is to provide plug-and-play facilities for UNIX-like desktops with focus on providing a rich and extensible description of device characteristics and features. HAL has no other major dependencies apart from D-BUS which, given sufficient infrastructure, allows it to be implemented on many UNIX-like systems. The major focus, initially, is systems running the Linux 2.6 series kernels.

D-BUS is a simple IPC (interprocess communication) protocol that allows asynchronous messages to be dispatched either through a daemon process (hub model), or directly between applications (peer-to-peer model). HAL exports two D-BUS interfaces: "org.freedesktop.Hal.Manager" for locating device objects and "org.freedesktop.Hal.Device" for individual device object services.

See also a high-level diagram of the HAL architecture.

Some important observations:

3. Properties

Properties are structured into namespaces, separated by dot (.). E.g. storage.cdrom.read_speed property is part of the storage.cdrom namespace, which in turn is part of the storage namespace. Below are a few examples of properties from various namespaces (full list in the spec):

namespaceproperties
infobus, udi, persistent, capabilities, product, vendor, parent, locked
usb_devicedevice_class, speed_bcd, serial, product, vendor
scsihost, bus, target, lun
blockdevice, major, minor, is_volume, no_partitions
volumeis_mounted, mount_point, fstype, label, uuid, block_size, size, is_partition
storagebus, drive_type, removable, hotpluggable, requires_eject, media_check_enabled, automount_enabled_hint
storage.cdromcdr, cdrw, dvd, dvdr, ..., read_speed, write_speed
storage.policy.defaultmount_option.*, mount_root
cameraaccess_method, libgphoto2.support

Here are two examples of HAL objects, taken with lshal command on Ubuntu Linux with HAL 0.5.2. Here's how a UHCI controller looks like:

udi = '/org/freedesktop/Hal/devices/usb_device_0_0_0000_00_1d_1'
  info.udi = '/org/freedesktop/Hal/devices/usb_device_0_0_0000_00_1d_1'  (string)
  linux.subsystem = 'usb'  (string)
  linux.hotplug_type = 1  (0x1)  (int)
  usb_device.bus_number = 2  (0x2)  (int)
  usb_device.can_wake_up = false  (bool)
  usb_device.is_self_powered = true  (bool)
  usb_device.version_bcd = 272  (0x110)  (int)
  usb_device.speed_bcd = 4608  (0x1200)  (int)
  usb_device.serial = '0000:00:1d.1'  (string)
  usb_device.linux.device_number = 1  (0x1)  (int)
  usb_device.num_ports = 2  (0x2)  (int)
  usb_device.max_power = 0  (0x0)  (int)
  usb_device.device_revision_bcd = 518  (0x206)  (int)
  info.product = 'Intel Corp. 82801CA/CAM USB (Hub #2)'  (string)
  usb_device.product = 'Intel Corp. 82801CA/CAM USB (Hub #2)'  (string)
  info.vendor = 'Linux 2.6.10-5-386 uhci_hcd'  (string)
  usb_device.vendor = 'Linux 2.6.10-5-386 uhci_hcd'  (string)
  usb_device.product_id = 0  (0x0)  (int)
  usb_device.vendor_id = 0  (0x0)  (int)
  usb_device.device_protocol = 0  (0x0)  (int)
  usb_device.device_subclass = 0  (0x0)  (int)
  usb_device.device_class = 9  (0x9)  (int)
  usb_device.num_interfaces = 1  (0x1)  (int)
  usb_device.num_configurations = 1  (0x1)  (int)
  usb_device.configuration_value = 1  (0x1)  (int)
  usb_device.linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:1d.1/usb2'  (string)
  info.linux.driver = 'usb'  (string)
  info.bus = 'usb_device'  (string)
  info.parent = '/org/freedesktop/Hal/devices/pci_8086_2484'  (string)
  linux.sysfs_path_device = '/sys/devices/pci0000:00/0000:00:1d.1/usb2'  (string)
  linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:1d.1/usb2'  (string)

This is the IDE hard drive:

udi = '/org/freedesktop/Hal/devices/storage_model_MSC_U02'
  info.addons = {'hald-addon-storage'} (string list)
  storage.policy.should_mount = true  (bool)
  block.storage_device = '/org/freedesktop/Hal/devices/storage_model_MSC_U02'  (string)
  info.udi = '/org/freedesktop/Hal/devices/storage_model_MSC_U02'  (string)
  storage.requires_eject = false  (bool)
  storage.hotpluggable = true  (bool)
  info.capabilities = {'storage', 'block'} (string list)
  info.category = 'storage'  (string)
  info.product = 'MSC-U02'  (string)
  info.vendor = 'Sony'  (string)
  storage.removable = true  (bool)
  storage.physical_device = '/org/freedesktop/Hal/devices/usb_device_54c_56_noserial_if0'  (string)
  storage.lun = 0  (0x0)  (int)
  storage.drive_type = 'disk'  (string)
  storage.vendor = 'Sony'  (string)
  storage.model = 'MSC-U02'  (string)
  storage.automount_enabled_hint = true  (bool)
  storage.media_check_enabled = true  (bool)
  storage.no_partitions_hint = false  (bool)
  storage.bus = 'usb'  (string)
  block.is_volume = false  (bool)
  block.minor = 0  (0x0)  (int)
  block.major = 8  (0x8)  (int)
  block.device = '/dev/sda'  (string)
  linux.hotplug_type = 3  (0x3)  (int)
  info.parent = '/org/freedesktop/Hal/devices/usb_device_54c_56_noserial_if0_scsi_host_scsi_device_lun0'  (string)
  linux.sysfs_path_device = '/sys/block/sda'  (string)
  linux.sysfs_path = '/sys/block/sda'  (string)

4. Installed components

HAL is usually installed either in / or /usr/local. Following is the contents of HAL 0.5.2:

sbin/haldHAL daemon
bin/lshalprint device objects known to HAL to stdout in human-readable format
bin/hal-device-managerGUI version of lshal, written in Python
bin/hal-{get,set}-propertyget/set device properties from scripts
lib/libhal.soHAL API
lib/libhal-storage.soHAL convenience library for storage devices and volumes
libexec/hald-probe-input
libexec/hald-probe-hiddev
libexec/hald-probe-storage
libexec/hald-probe-volume
libexec/hald-probe-printer
libexec/hald-probe-pc-floppy
libexec/hald-probe-smbios
libexec/hald-addon-hid-ups
libexec/hald-addon-acpi
libexec/hald-addon-storage
libexec/hald-addon-pmu
libexec/hal.hotplug
various helper programs
etc/dbus/system.d/hal.confD-BUS security policy for HAL
share/hal/fdi/directory for .fdi files
etc/hal/configuration and scripts

And these are programs that depend on libhal.so in the Ubuntu distro:

5. HAL interfaces

It is easy to get confused about HAL interfaces by looking at what interfaces HAL clients are using, for instance:

$ objdump -T gnome-volume-manager | grep -i hal
00000000      DF *UND*  00000092              hal_device_property_watch_all
00000000      DF *UND*  000001b8              hal_device_property_exists
00000000      DF *UND*  00000026              hal_free_string
00000000      DF *UND*  00000205              hal_find_device_by_capability
00000000      DF *UND*  00000206              hal_get_all_devices
00000000      DF *UND*  0000006d              hal_device_query_capability
00000000      DF *UND*  00000239              hal_device_get_property_string
00000000      DF *UND*  0000020f              hal_initialize
00000000      DF *UND*  000000a6              hal_shutdown
00000000      DF *UND*  00000050              hal_free_string_array
00000000      DF *UND*  00000252              hal_device_get_property_bool

These functions are part of libhal. However, this C API is only a collection of helper routines. The real HAL API is defined in terms of D-BUS methods, signals, interfaces and properties. For each D-BUS method and signal there is a corresponding libhal function, e.g. libhal_get_all_devices() corresponds to the "org.freedesktop.Hal.Manager"/"GetAllDevices" method.

There are other D-BUS bindings than C: Python, Java, Perl, etc. Any of these can be used to talk to the HAL daemon.

It is important to note that before HAL reaches 1.0 it does not guarantee any backwards compatibility whatsoever. In fact, HAL interfaces have changed dramatically in the past few months. Current rev is 0.5.6.

But C is still the dominant language for system programming in Solaris, and used by most volume managers, so I'm including a short summary of libhal.h and libhal-storage.h interfaces below.

Callback types
LibHalDeviceAdded
LibHalDeviceRemoved
LibHalDeviceNewCapability
LibHalDeviceLostCapability
LibHalDevicePropertyModified
LibHalDeviceCondition
Context functions
libhal_ctx_{new,free}Get/release a connection context handle
libhal_ctx_{init,shutdown}Establish a D-BUS connection with hald
libhal_ctx_init_directEstablish a peer-to-peer D-BUS connection with hald
Device functions
libhal_get_all_devicesGet all devices in the Global Device List (GDL)
libhal_device_exists
libhal_device_{get,set}_property_ {int,uint64,double,bool,string,strlist}
libhal_device_remove_property
libhal_psi_*property set iterator functions
libhal_agent_new_deviceCreate a new device object which will be hidden from applications until libhal_agent_commit_to_gdl() is called.
libhal_agent_remove_deviceCalled when device is removed. Device object removed from GDL unless info.persistent property is set.
libhal_agent_merge_propertiesMerge properties from one device to another
libhal_device_{lock,unlock}Take/release an advisory lock on the device
libhal_device_rescan
libhal_device_reprobe
libhal-storage "convenience" functions
libhal_storage_policy_{new,free}
libhal_drive_is_hotpluggable
libhal_drive_uses_removable_media
libhal_drive_requires_eject
libhal_drive_get_{type,bus,model,vendor,...}
libhal_drive_policy_compute_{display,icon}_name
libhal_drive_policy_is_mountable
libhal_drive_policy_get_{desired_mount_point, mount_options, mount_fs}
libhal_volume_get_{size, fstype, fsusage, partition_number, label, mount_point, uuid, ...}
libhal_volume_is_{mounted, partiotion, disc}
libhal_volume_disc_has_{audio,data}
libhal_volume_disc_is_{blank, rewritable, appendable}
...

7. Hotplug and coldplug

udev(8) facility maintains the /dev namespace in Linux similarly to devfsadm(1M) in Solaris.

Linux kernel subsystems that support hotplugging trigger /sbin/hotplug helper with a bunch of env variables set: ACTION (add or remove), SUBSYSTEM (usb, pci), DEVPATH and a few others. Hotplug helper then invokes programs located in /etc/hotplug.d. udev installs a handler in /etc/hotplug.d, so /dev is dynamically updated as devices come and go.

As udev creates or removes /dev entries, it runs programs located in /etc/dev.d. HAL installs a handler in /etc/dev.d, hal.hotplug, which sends a message to hald daemon over datagram socket. See hald/linux2/osspec.c:osspec_init() for socket initialization and hald/linux2/osspec.c:hald_helper_data() for how the data is processed.

When HAL starts up, it has to discover devices that already exist. It does so by getting a list of devices from udev and synthesizing a hotplug event for each of them. See hald/linux2/coldplug.c.

8. Sources of device information

HAL collects device information from two main sources: sysfs and probing.

8.1 sysfs

HAL creates many device properties from sysfs - a filesystem that presents device information as a filesystem. E.g. UHCI controller, whose properties were shown in section 3 above, has linux.sysfs_device property set to /sys/devices/pci0000:00/0000:00:1d.1/usb2:

$ cd /sys/devices/pci0000:00/0000:00:1d.1/usb2
$ ls -l
total 0
drwxr-xr-x  3 root root    0 2005-07-10 22:51 2-0:1.0
-r--r--r--  1 root root 4096 2005-07-10 18:16 bcdDevice
-rw-r--r--  1 root root 4096 2005-07-10 22:51 bConfigurationValue
-r--r--r--  1 root root 4096 2005-07-10 18:16 bDeviceClass
-r--r--r--  1 root root 4096 2005-07-10 22:51 bDeviceProtocol
-r--r--r--  1 root root 4096 2005-07-10 22:51 bDeviceSubClass
-r--r--r--  1 root root 4096 2005-07-10 22:51 bmAttributes
-r--r--r--  1 root root 4096 2005-07-10 22:51 bMaxPower
-r--r--r--  1 root root 4096 2005-07-10 18:16 bNumConfigurations
-r--r--r--  1 root root 4096 2005-07-10 22:51 bNumInterfaces
-r--r--r--  1 root root 4096 2005-07-10 22:51 configuration
-rw-r--r--  1 root root 4096 2005-07-10 22:51 detach_state
-r--r--r--  1 root root 4096 2005-07-10 18:16 devnum
lrwxrwxrwx  1 root root    0 2005-07-10 18:16 driver -> ../../../../bus/usb/drivers/usb
-r--r--r--  1 root root 4096 2005-07-10 18:16 idProduct
-r--r--r--  1 root root 4096 2005-07-10 18:16 idVendor
-r--r--r--  1 root root 4096 2005-07-10 22:51 manufacturer
-r--r--r--  1 root root 4096 2005-07-10 22:51 maxchild
drwxr-xr-x  2 root root    0 2005-07-10 22:51 power
-r--r--r--  1 root root 4096 2005-07-10 22:51 product
-r--r--r--  1 root root 4096 2005-07-10 22:51 serial
-r--r--r--  1 root root 4096 2005-07-10 22:51 speed
-r--r--r--  1 root root 4096 2005-07-10 22:51 version
$ cat bDeviceClass bDeviceProtocol
09
00
$ cd "2-0:1.0"
$ ls -l
total 0
-r--r--r--  1 root root 4096 2005-07-10 22:51 bAlternateSetting
-r--r--r--  1 root root 4096 2005-07-10 18:16 bInterfaceClass
-r--r--r--  1 root root 4096 2005-07-10 22:51 bInterfaceNumber
-r--r--r--  1 root root 4096 2005-07-10 18:16 bInterfaceProtocol
-r--r--r--  1 root root 4096 2005-07-10 18:16 bInterfaceSubClass
-r--r--r--  1 root root 4096 2005-07-10 18:16 bNumEndpoints
-rw-r--r--  1 root root 4096 2005-07-10 22:51 detach_state
lrwxrwxrwx  1 root root    0 2005-07-10 18:16 driver -> ../../../../../bus/usb/drivers/hub
drwxr-xr-x  2 root root    0 2005-07-10 22:51 power

Solaris has libdevinfo(3LIB) for this purpose. Sun Ray has its own APIs.

8.2 Probing

Probes are special programs used to probe a device and fill out its properties. HAL forks a process for each device probe. Arguments are passed through environment variables; probes call libhal functions to set properties; and probe's exit status has a special meaning. HAL passes at least UDI and device path to the probe. Probe usually opens the device and issues OS-specific ioctls to collect information about the device. Examples of Linux HAL probes:

9. Addons and callouts

Addons are used for various purposes, e.g.:

Callouts are programs that are invoked when: