From f24a2794e1b527e45efbbcad1c272f176f3d9df0 Mon Sep 17 00:00:00 2001 From: Aaron Young Date: Wed, 27 Oct 2021 16:05:43 -0700 Subject: [virtio] Update driver to use DMA API Signed-off-by: Aaron Young --- src/drivers/bus/virtio-pci.c | 45 ++++++++++++++++++++++++++++-------------- src/drivers/bus/virtio-ring.c | 4 ++-- src/drivers/net/virtio-net.c | 36 ++++++++++++++++++++++----------- src/include/ipxe/virtio-pci.h | 8 ++++++-- src/include/ipxe/virtio-ring.h | 7 ++++++- 5 files changed, 69 insertions(+), 31 deletions(-) diff --git a/src/drivers/bus/virtio-pci.c b/src/drivers/bus/virtio-pci.c index 5d2d62750..8b34c7277 100644 --- a/src/drivers/bus/virtio-pci.c +++ b/src/drivers/bus/virtio-pci.c @@ -17,37 +17,47 @@ #include "ipxe/io.h" #include "ipxe/iomap.h" #include "ipxe/pci.h" +#include "ipxe/dma.h" #include "ipxe/reboot.h" #include "ipxe/virtio-pci.h" #include "ipxe/virtio-ring.h" -static int vp_alloc_vq(struct vring_virtqueue *vq, u16 num) +static int vp_alloc_vq(struct vring_virtqueue *vq, u16 num, size_t header_size) { - size_t queue_size = PAGE_MASK + vring_size(num); + size_t ring_size = PAGE_MASK + vring_size(num); size_t vdata_size = num * sizeof(void *); + size_t queue_size = ring_size + vdata_size + header_size; - vq->queue = zalloc(queue_size + vdata_size); + vq->queue = dma_alloc(vq->dma, &vq->map, queue_size, queue_size); if (!vq->queue) { return -ENOMEM; } + memset ( vq->queue, 0, queue_size ); + vq->queue_size = queue_size; + /* vdata immediately follows the ring */ - vq->vdata = (void **)(vq->queue + queue_size); + vq->vdata = (void **)(vq->queue + ring_size); + + /* empty header immediately follows vdata */ + vq->empty_header = (struct virtio_net_hdr_modern *)(vq->queue + ring_size + vdata_size); return 0; } void vp_free_vq(struct vring_virtqueue *vq) { - if (vq->queue) { - free(vq->queue); + if (vq->queue && vq->queue_size) { + dma_free(&vq->map, vq->queue, vq->queue_size); vq->queue = NULL; vq->vdata = NULL; + vq->queue_size = 0; } } int vp_find_vq(unsigned int ioaddr, int queue_index, - struct vring_virtqueue *vq) + struct vring_virtqueue *vq, struct dma_device *dma_dev, + size_t header_size) { struct vring * vr = &vq->vring; u16 num; @@ -73,9 +83,10 @@ int vp_find_vq(unsigned int ioaddr, int queue_index, } vq->queue_index = queue_index; + vq->dma = dma_dev; /* initialize the queue */ - rc = vp_alloc_vq(vq, num); + rc = vp_alloc_vq(vq, num, header_size); if (rc) { DBG("VIRTIO-PCI ERROR: failed to allocate queue memory\n"); return rc; @@ -87,8 +98,7 @@ int vp_find_vq(unsigned int ioaddr, int queue_index, * NOTE: vr->desc is initialized by vring_init() */ - outl((unsigned long)virt_to_phys(vr->desc) >> PAGE_SHIFT, - ioaddr + VIRTIO_PCI_QUEUE_PFN); + outl(dma(&vq->map, vr->desc) >> PAGE_SHIFT, ioaddr + VIRTIO_PCI_QUEUE_PFN); return num; } @@ -348,7 +358,8 @@ void vpm_notify(struct virtio_pci_modern_device *vdev, } int vpm_find_vqs(struct virtio_pci_modern_device *vdev, - unsigned nvqs, struct vring_virtqueue *vqs) + unsigned nvqs, struct vring_virtqueue *vqs, + struct dma_device *dma_dev, size_t header_size) { unsigned i; struct vring_virtqueue *vq; @@ -392,11 +403,12 @@ int vpm_find_vqs(struct virtio_pci_modern_device *vdev, vq = &vqs[i]; vq->queue_index = i; + vq->dma = dma_dev; /* get offset of notification word for this vq */ off = vpm_ioread16(vdev, &vdev->common, COMMON_OFFSET(queue_notify_off)); - err = vp_alloc_vq(vq, size); + err = vp_alloc_vq(vq, size, header_size); if (err) { DBG("VIRTIO-PCI %p: failed to allocate queue memory\n", vdev); return err; @@ -406,13 +418,16 @@ int vpm_find_vqs(struct virtio_pci_modern_device *vdev, /* activate the queue */ vpm_iowrite16(vdev, &vdev->common, size, COMMON_OFFSET(queue_size)); - vpm_iowrite64(vdev, &vdev->common, virt_to_phys(vq->vring.desc), + vpm_iowrite64(vdev, &vdev->common, + dma(&vq->map, vq->vring.desc), COMMON_OFFSET(queue_desc_lo), COMMON_OFFSET(queue_desc_hi)); - vpm_iowrite64(vdev, &vdev->common, virt_to_phys(vq->vring.avail), + vpm_iowrite64(vdev, &vdev->common, + dma(&vq->map, vq->vring.avail), COMMON_OFFSET(queue_avail_lo), COMMON_OFFSET(queue_avail_hi)); - vpm_iowrite64(vdev, &vdev->common, virt_to_phys(vq->vring.used), + vpm_iowrite64(vdev, &vdev->common, + dma(&vq->map, vq->vring.used), COMMON_OFFSET(queue_used_lo), COMMON_OFFSET(queue_used_hi)); diff --git a/src/drivers/bus/virtio-ring.c b/src/drivers/bus/virtio-ring.c index 98e787e16..e448c3488 100644 --- a/src/drivers/bus/virtio-ring.c +++ b/src/drivers/bus/virtio-ring.c @@ -98,7 +98,7 @@ void vring_add_buf(struct vring_virtqueue *vq, for (i = head; out; i = vr->desc[i].next, out--) { vr->desc[i].flags = VRING_DESC_F_NEXT; - vr->desc[i].addr = (u64)virt_to_phys(list->addr); + vr->desc[i].addr = list->addr; vr->desc[i].len = list->length; prev = i; list++; @@ -106,7 +106,7 @@ void vring_add_buf(struct vring_virtqueue *vq, for ( ; in; i = vr->desc[i].next, in--) { vr->desc[i].flags = VRING_DESC_F_NEXT|VRING_DESC_F_WRITE; - vr->desc[i].addr = (u64)virt_to_phys(list->addr); + vr->desc[i].addr = list->addr; vr->desc[i].len = list->length; prev = i; list++; diff --git a/src/drivers/net/virtio-net.c b/src/drivers/net/virtio-net.c index 78ec9ac4e..0c4541924 100644 --- a/src/drivers/net/virtio-net.c +++ b/src/drivers/net/virtio-net.c @@ -29,6 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include #include @@ -99,8 +100,9 @@ struct virtnet_nic { /** Pending rx packet count */ unsigned int rx_num_iobufs; - /** Virtio net dummy packet headers */ - struct virtio_net_hdr_modern empty_header[QUEUE_NB]; + /** DMA device */ + struct dma_device *dma; + }; /** Add an iobuf to a virtqueue @@ -115,7 +117,7 @@ static void virtnet_enqueue_iob ( struct net_device *netdev, int vq_idx, struct io_buffer *iobuf ) { struct virtnet_nic *virtnet = netdev->priv; struct vring_virtqueue *vq = &virtnet->virtqueue[vq_idx]; - struct virtio_net_hdr_modern *header = &virtnet->empty_header[vq_idx]; + struct virtio_net_hdr_modern *header = vq->empty_header; unsigned int out = ( vq_idx == TX_INDEX ) ? 2 : 0; unsigned int in = ( vq_idx == TX_INDEX ) ? 0 : 2; size_t header_len = ( virtnet->virtio_version ? @@ -132,11 +134,11 @@ static void virtnet_enqueue_iob ( struct net_device *netdev, * to header->flags for received packets. Work around * this by using separate RX and TX headers. */ - .addr = ( char* ) header, + .addr = dma ( &vq->map, header ), .length = header_len, }, { - .addr = ( char* ) iobuf->data, + .addr = iob_dma ( iobuf ), .length = iob_len ( iobuf ), }, }; @@ -161,7 +163,7 @@ static void virtnet_refill_rx_virtqueue ( struct net_device *netdev ) { struct io_buffer *iobuf; /* Try to allocate a buffer, stop for now if out of memory */ - iobuf = alloc_iob ( len ); + iobuf = alloc_rx_iob ( len, virtnet->dma ); if ( ! iobuf ) break; @@ -215,7 +217,8 @@ static int virtnet_open_legacy ( struct net_device *netdev ) { /* Initialize rx/tx virtqueues */ for ( i = 0; i < QUEUE_NB; i++ ) { - if ( vp_find_vq ( ioaddr, i, &virtnet->virtqueue[i] ) == -1 ) { + if ( vp_find_vq ( ioaddr, i, &virtnet->virtqueue[i], virtnet->dma, + sizeof ( struct virtio_net_hdr_modern ) ) == -1 ) { DBGC ( virtnet, "VIRTIO-NET %p cannot register queue %d\n", virtnet, i ); virtnet_free_virtqueues ( netdev ); @@ -280,7 +283,8 @@ static int virtnet_open_modern ( struct net_device *netdev ) { } /* Initialize rx/tx virtqueues */ - if ( vpm_find_vqs ( &virtnet->vdev, QUEUE_NB, virtnet->virtqueue ) ) { + if ( vpm_find_vqs ( &virtnet->vdev, QUEUE_NB, virtnet->virtqueue, + virtnet->dma, sizeof ( struct virtio_net_hdr_modern ) ) ) { DBGC ( virtnet, "VIRTIO-NET %p cannot register queues\n", virtnet ); virtnet_free_virtqueues ( netdev ); @@ -335,7 +339,7 @@ static void virtnet_close ( struct net_device *netdev ) { /* Free rx iobufs */ list_for_each_entry_safe ( iobuf, next_iobuf, &virtnet->rx_iobufs, list ) { - free_iob ( iobuf ); + free_rx_iob ( iobuf ); } INIT_LIST_HEAD ( &virtnet->rx_iobufs ); virtnet->rx_num_iobufs = 0; @@ -478,6 +482,12 @@ static int virtnet_probe_legacy ( struct pci_device *pci ) { /* Enable PCI bus master and reset NIC */ adjust_pci_device ( pci ); + + /* Configure DMA */ + virtnet->dma = &pci->dma; + dma_set_mask_64bit ( virtnet->dma ); + netdev->dma = virtnet->dma; + vp_reset ( ioaddr ); /* Load MAC address and MTU */ @@ -506,7 +516,7 @@ static int virtnet_probe_legacy ( struct pci_device *pci ) { return 0; unregister_netdev ( netdev ); - err_register_netdev: +err_register_netdev: vp_reset ( ioaddr ); netdev_nullify ( netdev ); netdev_put ( netdev ); @@ -586,6 +596,11 @@ static int virtnet_probe_modern ( struct pci_device *pci, int *found_dev ) { /* Enable the PCI device */ adjust_pci_device ( pci ); + /* Configure DMA */ + virtnet->dma = &pci->dma; + dma_set_mask_64bit ( virtnet->dma ); + netdev->dma = virtnet->dma; + /* Reset the device and set initial status bits */ vpm_reset ( &virtnet->vdev ); vpm_add_status ( &virtnet->vdev, VIRTIO_CONFIG_S_ACKNOWLEDGE ); @@ -633,7 +648,6 @@ err_mac_address: vpm_reset ( &virtnet->vdev ); netdev_nullify ( netdev ); netdev_put ( netdev ); - virtio_pci_unmap_capability ( &virtnet->vdev.device ); err_map_device: virtio_pci_unmap_capability ( &virtnet->vdev.isr ); diff --git a/src/include/ipxe/virtio-pci.h b/src/include/ipxe/virtio-pci.h index f3074f14d..7abae26ff 100644 --- a/src/include/ipxe/virtio-pci.h +++ b/src/include/ipxe/virtio-pci.h @@ -1,6 +1,8 @@ #ifndef _VIRTIO_PCI_H_ # define _VIRTIO_PCI_H_ +#include + /* A 32-bit r/o bitmask of the features supported by the host */ #define VIRTIO_PCI_HOST_FEATURES 0 @@ -198,7 +200,8 @@ struct vring_virtqueue; void vp_free_vq(struct vring_virtqueue *vq); int vp_find_vq(unsigned int ioaddr, int queue_index, - struct vring_virtqueue *vq); + struct vring_virtqueue *vq, struct dma_device *dma_dev, + size_t header_size); /* Virtio 1.0 I/O routines abstract away the three possible HW access @@ -298,7 +301,8 @@ void vpm_notify(struct virtio_pci_modern_device *vdev, struct vring_virtqueue *vq); int vpm_find_vqs(struct virtio_pci_modern_device *vdev, - unsigned nvqs, struct vring_virtqueue *vqs); + unsigned nvqs, struct vring_virtqueue *vqs, + struct dma_device *dma_dev, size_t header_size); int virtio_pci_find_capability(struct pci_device *pci, uint8_t cfg_type); diff --git a/src/include/ipxe/virtio-ring.h b/src/include/ipxe/virtio-ring.h index 852769f29..d082139e9 100644 --- a/src/include/ipxe/virtio-ring.h +++ b/src/include/ipxe/virtio-ring.h @@ -2,6 +2,7 @@ # define _VIRTIO_RING_H_ #include +#include /* Status byte for guest to report progress, and synchronize features. */ /* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */ @@ -74,17 +75,21 @@ struct vring { struct vring_virtqueue { unsigned char *queue; + size_t queue_size; + struct dma_mapping map; + struct dma_device *dma; struct vring vring; u16 free_head; u16 last_used_idx; void **vdata; + struct virtio_net_hdr_modern *empty_header; /* PCI */ int queue_index; struct virtio_pci_region notification; }; struct vring_list { - char *addr; + physaddr_t addr; unsigned int length; }; -- cgit v1.2.3-55-g7522 From 85eb961bf9afd61385430f8837485ba69cc0cc11 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 28 Oct 2021 00:38:02 +0100 Subject: [xhci] Allow for permanent failure of the command mechanism Some xHCI controllers (observed with the Thunderbolt ports on a ThinkPad X1 Extreme Gen3 and a ThinkPad P53) seem to suffer a catastrophic failure at the point that ExitBootServices() is called if the IOMMU is enabled. The symptoms appear to be consistent with another UEFI driver (e.g. the IOMMU driver, or the Thunderbolt driver) having torn down the DMA mappings, leaving the xHCI controller unable to write to host memory. The observable effect is that all commands fail with a timeout, and attempts to abort command execution similarly fail since the xHCI controller is unable to report the abort completion. Check for failure to abort a command, and respond by performing a full device reset (as recommended by the xHCI specification) and by marking the device as permanently failed. Reported-by: Andreas Hammarskjöld Signed-off-by: Michael Brown --- src/drivers/usb/xhci.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/drivers/usb/xhci.h | 2 ++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/drivers/usb/xhci.c b/src/drivers/usb/xhci.c index cc48af033..3d98b1e10 100644 --- a/src/drivers/usb/xhci.c +++ b/src/drivers/usb/xhci.c @@ -1165,6 +1165,31 @@ static int xhci_reset ( struct xhci_device *xhci ) { return -ETIMEDOUT; } +/** + * Mark xHCI device as permanently failed + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static int xhci_fail ( struct xhci_device *xhci ) { + size_t len; + int rc; + + /* Mark command mechanism as permanently failed */ + xhci->failed = 1; + + /* Reset device */ + if ( ( rc = xhci_reset ( xhci ) ) != 0 ) + return rc; + + /* Discard DCBAA entries since DCBAAP has been cleared */ + assert ( xhci->dcbaa.context != NULL ); + len = ( ( xhci->slots + 1 ) * sizeof ( xhci->dcbaa.context[0] ) ); + memset ( xhci->dcbaa.context, 0, len ); + + return 0; +} + /****************************************************************************** * * Transfer request blocks @@ -1720,6 +1745,10 @@ static void xhci_event_poll ( struct xhci_device *xhci ) { unsigned int consumed; unsigned int type; + /* Do nothing if device has permanently failed */ + if ( xhci->failed ) + return; + /* Poll for events */ profile_start ( &xhci_event_profiler ); for ( consumed = 0 ; ; consumed++ ) { @@ -1778,6 +1807,7 @@ static void xhci_event_poll ( struct xhci_device *xhci ) { */ static void xhci_abort ( struct xhci_device *xhci ) { physaddr_t crp; + uint32_t crcr; /* Abort the command */ DBGC2 ( xhci, "XHCI %s aborting command\n", xhci->name ); @@ -1786,8 +1816,18 @@ static void xhci_abort ( struct xhci_device *xhci ) { /* Allow time for command to abort */ mdelay ( XHCI_COMMAND_ABORT_DELAY_MS ); - /* Sanity check */ - assert ( ( readl ( xhci->op + XHCI_OP_CRCR ) & XHCI_CRCR_CRR ) == 0 ); + /* Check for failure to abort */ + crcr = readl ( xhci->op + XHCI_OP_CRCR ); + if ( crcr & XHCI_CRCR_CRR ) { + + /* Device has failed to abort a command and is almost + * certainly beyond repair. Reset device, abandoning + * all state, and mark device as failed to avoid + * delays on any future command attempts. + */ + DBGC ( xhci, "XHCI %s failed to abort command\n", xhci->name ); + xhci_fail ( xhci ); + } /* Consume (and ignore) any final command status */ xhci_event_poll ( xhci ); @@ -1813,6 +1853,12 @@ static int xhci_command ( struct xhci_device *xhci, union xhci_trb *trb ) { unsigned int i; int rc; + /* Immediately fail all commands if command mechanism has failed */ + if ( xhci->failed ) { + rc = -EPIPE; + goto err_failed; + } + /* Sanity check */ if ( xhci->pending ) { DBGC ( xhci, "XHCI %s command ring busy\n", xhci->name ); @@ -1863,6 +1909,7 @@ static int xhci_command ( struct xhci_device *xhci, union xhci_trb *trb ) { err_enqueue: xhci->pending = NULL; err_pending: + err_failed: return rc; } diff --git a/src/drivers/usb/xhci.h b/src/drivers/usb/xhci.h index 6e02d70e0..a3c8888af 100644 --- a/src/drivers/usb/xhci.h +++ b/src/drivers/usb/xhci.h @@ -1115,6 +1115,8 @@ struct xhci_device { struct xhci_event_ring event; /** Current command (if any) */ union xhci_trb *pending; + /** Command mechanism has permanently failed */ + int failed; /** Device slots, indexed by slot ID */ struct xhci_slot **slot; -- cgit v1.2.3-55-g7522 From 1844aacc837bf81cb1959fa65f2e52dcc70a0cae Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 11 Nov 2021 23:31:23 +0000 Subject: [uri] Retain original encodings for path, query, and fragment fields iPXE decodes any percent-encoded characters during the URI parsing stage, thereby allowing protocol implementations to consume the raw field values directly without further decoding. When reconstructing a URI string for use in an HTTP request line, the percent-encoding is currently reapplied in a reversible way: we guarantee that our reconstructed URI string could be decoded to give the same raw field values. This technically violates RFC3986, which states that "URIs that differ in the replacement of a reserved character with its corresponding percent-encoded octet are not equivalent". Experiments show that several HTTP server applications will attach meaning to the choice of whether or not a particular character was percent-encoded, even when the percent-encoding is unnecessary from the perspective of parsing the URI into its component fields. Fix by storing the originally encoded substrings for the path, query, and fragment fields and using these original encoded versions when reconstructing a URI string. The path field is also stored as a decoded string, for use by protocols such as TFTP that communicate using raw strings rather than URI-encoded strings. All other fields (such as the username and password) continue to be stored only in their decoded versions since nothing ever needs to know the originally encoded versions of these fields. Signed-off-by: Michael Brown --- src/core/uri.c | 131 ++++++++++++++++++++++++++++++------------------- src/include/ipxe/uri.h | 31 +++++++++--- src/net/tcp/httpcore.c | 4 +- src/tests/uri_test.c | 53 +++++++++++++++----- src/usr/imgmgmt.c | 4 +- 5 files changed, 148 insertions(+), 75 deletions(-) diff --git a/src/core/uri.c b/src/core/uri.c index e9e512ab4..a0f79e9ec 100644 --- a/src/core/uri.c +++ b/src/core/uri.c @@ -79,12 +79,10 @@ size_t uri_decode ( const char *encoded, void *buf, size_t len ) { /** * Decode URI field in-place * - * @v uri URI - * @v field URI field index + * @v encoded Encoded field, or NULL */ -static void uri_decode_inplace ( struct uri *uri, unsigned int field ) { - const char *encoded = uri_field ( uri, field ); - char *decoded = ( ( char * ) encoded ); +static void uri_decode_inplace ( char *encoded ) { + char *decoded = encoded; size_t len; /* Do nothing if field is not present */ @@ -150,7 +148,7 @@ static int uri_character_escaped ( char c, unsigned int field ) { * parser but for any other URI parsers (e.g. HTTP query * string parsers, which care about '=' and '&'). */ - static const char *escaped[URI_FIELDS] = { + static const char *escaped[URI_EPATH] = { /* Scheme or default: escape everything */ [URI_SCHEME] = "/#:@?=&", /* Opaque part: escape characters which would affect @@ -172,20 +170,21 @@ static int uri_character_escaped ( char c, unsigned int field ) { * appears within paths. */ [URI_PATH] = "#:@?", - /* Query: escape everything except '/', which - * sometimes appears within queries. - */ - [URI_QUERY] = "#:@?", - /* Fragment: escape everything */ - [URI_FRAGMENT] = "/#:@?", }; - return ( /* Always escape non-printing characters and whitespace */ - ( ! isprint ( c ) ) || ( c == ' ' ) || - /* Always escape '%' */ - ( c == '%' ) || - /* Escape field-specific characters */ - strchr ( escaped[field], c ) ); + /* Always escape non-printing characters and whitespace */ + if ( ( ! isprint ( c ) ) || ( c == ' ' ) ) + return 1; + + /* Escape nothing else in already-escaped fields */ + if ( field >= URI_EPATH ) + return 0; + + /* Escape '%' and any field-specific characters */ + if ( ( c == '%' ) || strchr ( escaped[field], c ) ) + return 1; + + return 0; } /** @@ -262,10 +261,12 @@ static void uri_dump ( const struct uri *uri ) { DBGC ( uri, " port \"%s\"", uri->port ); if ( uri->path ) DBGC ( uri, " path \"%s\"", uri->path ); - if ( uri->query ) - DBGC ( uri, " query \"%s\"", uri->query ); - if ( uri->fragment ) - DBGC ( uri, " fragment \"%s\"", uri->fragment ); + if ( uri->epath ) + DBGC ( uri, " epath \"%s\"", uri->epath ); + if ( uri->equery ) + DBGC ( uri, " equery \"%s\"", uri->equery ); + if ( uri->efragment ) + DBGC ( uri, " efragment \"%s\"", uri->efragment ); if ( uri->params ) DBGC ( uri, " params \"%s\"", uri->params->name ); } @@ -298,17 +299,19 @@ struct uri * parse_uri ( const char *uri_string ) { char *raw; char *tmp; char *path; + char *epath; char *authority; size_t raw_len; unsigned int field; - /* Allocate space for URI struct and a copy of the string */ + /* Allocate space for URI struct and two copies of the string */ raw_len = ( strlen ( uri_string ) + 1 /* NUL */ ); - uri = zalloc ( sizeof ( *uri ) + raw_len ); + uri = zalloc ( sizeof ( *uri ) + ( 2 * raw_len ) ); if ( ! uri ) return NULL; ref_init ( &uri->refcnt, uri_free ); raw = ( ( ( void * ) uri ) + sizeof ( *uri ) ); + path = ( raw + raw_len ); /* Copy in the raw string */ memcpy ( raw, uri_string, raw_len ); @@ -328,7 +331,7 @@ struct uri * parse_uri ( const char *uri_string ) { /* Chop off the fragment, if it exists */ if ( ( tmp = strchr ( raw, '#' ) ) ) { *(tmp++) = '\0'; - uri->fragment = tmp; + uri->efragment = tmp; } /* Identify absolute/relative URI */ @@ -338,47 +341,47 @@ struct uri * parse_uri ( const char *uri_string ) { *(tmp++) = '\0'; if ( *tmp == '/' ) { /* Absolute URI with hierarchical part */ - path = tmp; + epath = tmp; } else { /* Absolute URI with opaque part */ uri->opaque = tmp; - path = NULL; + epath = NULL; } } else { /* Relative URI */ - path = raw; + epath = raw; } /* If we don't have a path (i.e. we have an absolute URI with * an opaque portion, we're already finished processing */ - if ( ! path ) + if ( ! epath ) goto done; /* Chop off the query, if it exists */ - if ( ( tmp = strchr ( path, '?' ) ) ) { + if ( ( tmp = strchr ( epath, '?' ) ) ) { *(tmp++) = '\0'; - uri->query = tmp; + uri->equery = tmp; } /* If we have no path remaining, then we're already finished * processing. */ - if ( ! path[0] ) + if ( ! epath[0] ) goto done; /* Identify net/absolute/relative path */ - if ( uri->scheme && ( strncmp ( path, "//", 2 ) == 0 ) ) { + if ( uri->scheme && ( strncmp ( epath, "//", 2 ) == 0 ) ) { /* Net path. If this is terminated by the first '/' * of an absolute path, then we have no space for a * terminator after the authority field, so shuffle * the authority down by one byte, overwriting one of * the two slashes. */ - authority = ( path + 2 ); + authority = ( epath + 2 ); if ( ( tmp = strchr ( authority, '/' ) ) ) { /* Shuffle down */ - uri->path = tmp; + uri->epath = tmp; memmove ( ( authority - 1 ), authority, ( tmp - authority ) ); authority--; @@ -386,10 +389,16 @@ struct uri * parse_uri ( const char *uri_string ) { } } else { /* Absolute/relative path */ - uri->path = path; + uri->epath = epath; authority = NULL; } + /* Create copy of path for decoding */ + if ( uri->epath ) { + strcpy ( path, uri->epath ); + uri->path = path; + } + /* If we don't have an authority (i.e. we have a non-net * path), we're already finished processing */ @@ -421,8 +430,8 @@ struct uri * parse_uri ( const char *uri_string ) { done: /* Decode fields in-place */ - for ( field = 0 ; field < URI_FIELDS ; field++ ) - uri_decode_inplace ( uri, field ); + for ( field = 0 ; field < URI_EPATH ; field++ ) + uri_decode_inplace ( ( char * ) uri_field ( uri, field ) ); DBGC ( uri, "URI parsed \"%s\" to", uri_string ); uri_dump ( uri ); @@ -458,8 +467,8 @@ size_t format_uri ( const struct uri *uri, char *buf, size_t len ) { static const char prefixes[URI_FIELDS] = { [URI_PASSWORD] = ':', [URI_PORT] = ':', - [URI_QUERY] = '?', - [URI_FRAGMENT] = '#', + [URI_EQUERY] = '?', + [URI_EFRAGMENT] = '#', }; char prefix; size_t used = 0; @@ -480,6 +489,10 @@ size_t format_uri ( const struct uri *uri, char *buf, size_t len ) { if ( ! uri_field ( uri, field ) ) continue; + /* Skip path field if encoded path is present */ + if ( ( field == URI_PATH ) && uri->epath ) + continue; + /* Prefix this field, if applicable */ prefix = prefixes[field]; if ( ( field == URI_HOST ) && ( uri->user != NULL ) ) @@ -676,6 +689,7 @@ char * resolve_path ( const char *base_path, struct uri * resolve_uri ( const struct uri *base_uri, struct uri *relative_uri ) { struct uri tmp_uri; + char *tmp_epath = NULL; char *tmp_path = NULL; struct uri *new_uri; @@ -685,20 +699,27 @@ struct uri * resolve_uri ( const struct uri *base_uri, /* Mangle URI */ memcpy ( &tmp_uri, base_uri, sizeof ( tmp_uri ) ); - if ( relative_uri->path ) { - tmp_path = resolve_path ( ( base_uri->path ? - base_uri->path : "/" ), - relative_uri->path ); + if ( relative_uri->epath ) { + tmp_epath = resolve_path ( ( base_uri->epath ? + base_uri->epath : "/" ), + relative_uri->epath ); + if ( ! tmp_epath ) + goto err_epath; + tmp_path = strdup ( tmp_epath ); + if ( ! tmp_path ) + goto err_path; + uri_decode_inplace ( tmp_path ); + tmp_uri.epath = tmp_epath; tmp_uri.path = tmp_path; - tmp_uri.query = relative_uri->query; - tmp_uri.fragment = relative_uri->fragment; + tmp_uri.equery = relative_uri->equery; + tmp_uri.efragment = relative_uri->efragment; tmp_uri.params = relative_uri->params; - } else if ( relative_uri->query ) { - tmp_uri.query = relative_uri->query; - tmp_uri.fragment = relative_uri->fragment; + } else if ( relative_uri->equery ) { + tmp_uri.equery = relative_uri->equery; + tmp_uri.efragment = relative_uri->efragment; tmp_uri.params = relative_uri->params; - } else if ( relative_uri->fragment ) { - tmp_uri.fragment = relative_uri->fragment; + } else if ( relative_uri->efragment ) { + tmp_uri.efragment = relative_uri->efragment; tmp_uri.params = relative_uri->params; } else if ( relative_uri->params ) { tmp_uri.params = relative_uri->params; @@ -707,7 +728,14 @@ struct uri * resolve_uri ( const struct uri *base_uri, /* Create demangled URI */ new_uri = uri_dup ( &tmp_uri ); free ( tmp_path ); + free ( tmp_epath ); return new_uri; + + free ( tmp_path ); + err_path: + free ( tmp_epath ); + err_epath: + return NULL; } /** @@ -746,6 +774,7 @@ static struct uri * tftp_uri ( struct sockaddr *sa_server, if ( asprintf ( &path, "/%s", filename ) < 0 ) goto err_path; tmp.path = path; + tmp.epath = path; /* Demangle URI */ uri = uri_dup ( &tmp ); diff --git a/src/include/ipxe/uri.h b/src/include/ipxe/uri.h index 3879a0e73..e5b7c8616 100644 --- a/src/include/ipxe/uri.h +++ b/src/include/ipxe/uri.h @@ -46,6 +46,20 @@ struct parameters; * scheme = "ftp", user = "joe", password = "secret", * host = "insecure.org", port = "8081", path = "/hidden/path/to", * query = "what=is", fragment = "this" + * + * The URI syntax includes a percent-encoding mechanism that can be + * used to represent characters that would otherwise not be possible, + * such as a '/' character within the password field. These encodings + * are decoded during the URI parsing stage, thereby allowing protocol + * implementations to consume the raw field values directly without + * further decoding. + * + * Some protocols (such as HTTP) communicate using URI-encoded values. + * For these protocols, the original encoded substring must be + * retained verbatim since the choice of whether or not to encode a + * particular character may have significance to the receiving + * application. We therefore retain the originally-encoded substrings + * for the path, query, and fragment fields. */ struct uri { /** Reference count */ @@ -62,12 +76,14 @@ struct uri { const char *host; /** Port number */ const char *port; - /** Path */ + /** Path (after URI decoding) */ const char *path; - /** Query */ - const char *query; - /** Fragment */ - const char *fragment; + /** Path (with original URI encoding) */ + const char *epath; + /** Query (with original URI encoding) */ + const char *equery; + /** Fragment (with original URI encoding) */ + const char *efragment; /** Form parameters */ struct parameters *params; } __attribute__ (( packed )); @@ -100,8 +116,9 @@ enum uri_fields { URI_HOST = URI_FIELD ( host ), URI_PORT = URI_FIELD ( port ), URI_PATH = URI_FIELD ( path ), - URI_QUERY = URI_FIELD ( query ), - URI_FRAGMENT = URI_FIELD ( fragment ), + URI_EPATH = URI_FIELD ( epath ), + URI_EQUERY = URI_FIELD ( equery ), + URI_EFRAGMENT = URI_FIELD ( efragment ), URI_FIELDS }; diff --git a/src/net/tcp/httpcore.c b/src/net/tcp/httpcore.c index 01bb496b2..fd94b5f08 100644 --- a/src/net/tcp/httpcore.c +++ b/src/net/tcp/httpcore.c @@ -614,8 +614,8 @@ int http_open ( struct interface *xfer, struct http_method *method, /* Calculate request URI length */ memset ( &request_uri, 0, sizeof ( request_uri ) ); - request_uri.path = ( uri->path ? uri->path : "/" ); - request_uri.query = uri->query; + request_uri.epath = ( uri->epath ? uri->epath : "/" ); + request_uri.equery = uri->equery; request_uri_len = ( format_uri ( &request_uri, NULL, 0 ) + 1 /* NUL */); diff --git a/src/tests/uri_test.c b/src/tests/uri_test.c index 92c2f9037..929ab3632 100644 --- a/src/tests/uri_test.c +++ b/src/tests/uri_test.c @@ -149,8 +149,10 @@ static void uri_okx ( struct uri *uri, struct uri *expected, const char *file, okx ( uristrcmp ( uri->host, expected->host ) == 0, file, line ); okx ( uristrcmp ( uri->port, expected->port ) == 0, file, line ); okx ( uristrcmp ( uri->path, expected->path ) == 0, file, line ); - okx ( uristrcmp ( uri->query, expected->query ) == 0, file, line ); - okx ( uristrcmp ( uri->fragment, expected->fragment ) == 0, file, line); + okx ( uristrcmp ( uri->epath, expected->epath ) == 0, file, line ); + okx ( uristrcmp ( uri->equery, expected->equery ) == 0, file, line ); + okx ( uristrcmp ( uri->efragment, expected->efragment ) == 0, + file, line); okx ( uri->params == expected->params, file, line ); } #define uri_ok( uri, expected ) uri_okx ( uri, expected, __FILE__, __LINE__ ) @@ -490,25 +492,33 @@ static struct uri_test uri_empty = { /** Basic HTTP URI */ static struct uri_test uri_boot_ipxe_org = { "http://boot.ipxe.org/demo/boot.php", - { .scheme = "http", .host = "boot.ipxe.org", .path = "/demo/boot.php" } + { .scheme = "http", .host = "boot.ipxe.org", + .path = "/demo/boot.php", .epath = "/demo/boot.php" }, }; /** Basic opaque URI */ static struct uri_test uri_mailto = { "mailto:ipxe-devel@lists.ipxe.org", - { .scheme = "mailto", .opaque = "ipxe-devel@lists.ipxe.org" } + { .scheme = "mailto", .opaque = "ipxe-devel@lists.ipxe.org" }, +}; + +/** Basic host-only URI */ +static struct uri_test uri_host = { + "http://boot.ipxe.org", + { .scheme = "http", .host = "boot.ipxe.org" }, }; /** Basic path-only URI */ static struct uri_test uri_path = { "/var/lib/tftpboot/pxelinux.0", - { .path = "/var/lib/tftpboot/pxelinux.0" }, + { .path = "/var/lib/tftpboot/pxelinux.0", + .epath ="/var/lib/tftpboot/pxelinux.0" }, }; /** Path-only URI with escaped characters */ static struct uri_test uri_path_escaped = { "/hello%20world%3F", - { .path = "/hello world?" }, + { .path = "/hello world?", .epath = "/hello%20world%3F" }, }; /** HTTP URI with all the trimmings */ @@ -521,8 +531,9 @@ static struct uri_test uri_http_all = { .host = "example.com", .port = "3001", .path = "/~foo/cgi-bin/foo.pl", - .query = "a=b&c=d", - .fragment = "bit", + .epath = "/~foo/cgi-bin/foo.pl", + .equery = "a=b&c=d", + .efragment = "bit", }, }; @@ -533,8 +544,9 @@ static struct uri_test uri_http_escaped = { .scheme = "https", .host = "test.ipxe.org", .path = "/wtf?\n", - .query = "kind#of/uri is", - .fragment = "this?", + .epath = "/wtf%3F%0A", + .equery = "kind%23of/uri%20is", + .efragment = "this%3F", }, }; @@ -550,8 +562,9 @@ static struct uri_test uri_http_escaped_improper = { .scheme = "https", .host = "test.ipxe.org", .path = "/wtf?\n", - .query = "kind#of/uri is", - .fragment = "this?", + .epath = "/wt%66%3f\n", + .equery = "kind%23of/uri is", + .efragment = "this?", }, }; @@ -562,6 +575,7 @@ static struct uri_test uri_ipv6 = { .scheme = "http", .host = "[2001:ba8:0:1d4::6950:5845]", .path = "/", + .epath = "/", }, }; @@ -573,6 +587,7 @@ static struct uri_test uri_ipv6_port = { .host = "[2001:ba8:0:1d4::6950:5845]", .port = "8001", .path = "/boot", + .epath = "/boot", }, }; @@ -583,6 +598,7 @@ static struct uri_test uri_ipv6_local = { .scheme = "http", .host = "[fe80::69ff:fe50:5845%net0]", .path = "/ipxe", + .epath = "/ipxe", }, }; @@ -598,6 +614,7 @@ static struct uri_test uri_ipv6_local_non_conforming = { .scheme = "http", .host = "[fe80::69ff:fe50:5845%net0]", .path = "/ipxe", + .epath = "/ipxe", }, }; @@ -625,6 +642,7 @@ static struct uri_test uri_file_absolute = { { .scheme = "file", .path = "/boot/script.ipxe", + .epath = "/boot/script.ipxe", }, }; @@ -635,6 +653,7 @@ static struct uri_test uri_file_volume = { .scheme = "file", .host = "hpilo", .path = "/boot/script.ipxe", + .epath = "/boot/script.ipxe", }, }; @@ -736,6 +755,7 @@ static struct uri_pxe_test uri_pxe_absolute = { .scheme = "http", .host = "not.a.tftp", .path = "/uri", + .epath = "/uri", }, "http://not.a.tftp/uri", }; @@ -754,6 +774,7 @@ static struct uri_pxe_test uri_pxe_absolute_path = { .scheme = "tftp", .host = "192.168.0.2", .path = "//absolute/path", + .epath = "//absolute/path", }, "tftp://192.168.0.2//absolute/path", }; @@ -772,6 +793,7 @@ static struct uri_pxe_test uri_pxe_relative_path = { .scheme = "tftp", .host = "192.168.0.3", .path = "/relative/path", + .epath = "/relative/path", }, "tftp://192.168.0.3/relative/path", }; @@ -790,8 +812,9 @@ static struct uri_pxe_test uri_pxe_icky = { .scheme = "tftp", .host = "10.0.0.6", .path = "/C:\\tftpboot\\icky#path", + .epath = "/C:\\tftpboot\\icky#path", }, - "tftp://10.0.0.6/C%3A\\tftpboot\\icky%23path", + "tftp://10.0.0.6/C:\\tftpboot\\icky#path", }; /** PXE URI with custom port */ @@ -810,6 +833,7 @@ static struct uri_pxe_test uri_pxe_port = { .host = "192.168.0.1", .port = "4069", .path = "//another/path", + .epath = "//another/path", }, "tftp://192.168.0.1:4069//another/path", }; @@ -873,6 +897,7 @@ static struct uri_params_test uri_params = { .scheme = "http", .host = "boot.ipxe.org", .path = "/demo/boot.php", + .epath = "/demo/boot.php", }, NULL, uri_params_list, @@ -902,6 +927,7 @@ static struct uri_params_test uri_named_params = { .host = "192.168.100.4", .port = "3001", .path = "/register", + .epath = "/register", }, "foo", uri_named_params_list, @@ -917,6 +943,7 @@ static void uri_test_exec ( void ) { uri_parse_format_dup_ok ( &uri_empty ); uri_parse_format_dup_ok ( &uri_boot_ipxe_org ); uri_parse_format_dup_ok ( &uri_mailto ); + uri_parse_format_dup_ok ( &uri_host ); uri_parse_format_dup_ok ( &uri_path ); uri_parse_format_dup_ok ( &uri_path_escaped ); uri_parse_format_dup_ok ( &uri_http_all ); diff --git a/src/usr/imgmgmt.c b/src/usr/imgmgmt.c index f8d149153..b7fc8293d 100644 --- a/src/usr/imgmgmt.c +++ b/src/usr/imgmgmt.c @@ -58,8 +58,8 @@ int imgdownload ( struct uri *uri, unsigned long timeout, memcpy ( &uri_redacted, uri, sizeof ( uri_redacted ) ); uri_redacted.user = NULL; uri_redacted.password = NULL; - uri_redacted.query = NULL; - uri_redacted.fragment = NULL; + uri_redacted.equery = NULL; + uri_redacted.efragment = NULL; uri_string_redacted = format_uri_alloc ( &uri_redacted ); if ( ! uri_string_redacted ) { rc = -ENOMEM; -- cgit v1.2.3-55-g7522 From 236299baa32452c79a59138c44eca5fcf4a918f9 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 12 Nov 2021 15:57:51 +0000 Subject: [xhci] Avoid DMA during shutdown if firmware has disabled bus mastering On some systems (observed with the Thunderbolt ports on a ThinkPad X1 Extreme Gen3 and a ThinkPad P53), the system firmware will disable bus mastering on the xHCI controller and all PCI bridges at the point that ExitBootServices() is called if the IOMMU is enabled. This leaves the xHCI controller unable to shut down cleanly since all commands will fail with a timeout. Commit 85eb961 ("[xhci] Allow for permanent failure of the command mechanism") allows us to detect that this has happened and respond cleanly. However, some unidentified hardware component (either the xHCI controller or one of the PCI bridges) seems to manage to enqueue the attempted DMA operation and eventually complete it after the operating system kernel has reenabled bus mastering. This results in a DMA operation to an area of memory that the hardware is no longer permitted to access. On Windows with the Driver Verifier enabled, this will result in a STOP 0xE6 (DRIVER_VERIFIER_DMA_VIOLATION). Work around this problem by detecting when bus mastering has been disabled, and immediately failing the device to avoid initiating any further DMA attempts. Reported-by: Andreas Hammarskjöld Signed-off-by: Michael Brown --- src/drivers/usb/xhci.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/drivers/usb/xhci.c b/src/drivers/usb/xhci.c index 3d98b1e10..3247ee69c 100644 --- a/src/drivers/usb/xhci.c +++ b/src/drivers/usb/xhci.c @@ -3459,14 +3459,36 @@ static int xhci_probe ( struct pci_device *pci ) { static void xhci_remove ( struct pci_device *pci ) { struct xhci_device *xhci = pci_get_drvdata ( pci ); struct usb_bus *bus = xhci->bus; + uint16_t command; + /* Some systems are observed to disable bus mastering on + * Thunderbolt controllers before we get a chance to shut + * down. Detect this and avoid attempting any DMA operations, + * which are guaranteed to fail and may end up spuriously + * completing after the operating system kernel starts up. + */ + pci_read_config_word ( pci, PCI_COMMAND, &command ); + if ( ! ( command & PCI_COMMAND_MASTER ) ) { + DBGC ( xhci, "XHCI %s DMA was disabled\n", xhci->name ); + xhci_fail ( xhci ); + } + + /* Unregister and free USB bus */ unregister_usb_bus ( bus ); free_usb_bus ( bus ); + + /* Reset device and undo any PCH-specific fixes */ xhci_reset ( xhci ); if ( xhci->quirks & XHCI_PCH ) xhci_pch_undo ( xhci, pci ); + + /* Release ownership back to BIOS */ xhci_legacy_release ( xhci ); + + /* Unmap registers */ iounmap ( xhci->regs ); + + /* Free device */ free ( xhci ); } -- cgit v1.2.3-55-g7522 From 51612b6e69e049b6f08e833581815b4d0e9ffd39 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 21 Nov 2021 13:19:55 +0000 Subject: [efi] Do not attempt to use console output after ExitBootServices() A successful call to ExitBootServices() will result in the EFI console becoming unusable. Ensure that the EFI wrapper produces a complete line of debug output before calling the wrapped ExitBootServices() method, and attempt subsequent debug output only if the call fails. Signed-off-by: Michael Brown --- src/interface/efi/efi_wrap.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/interface/efi/efi_wrap.c b/src/interface/efi/efi_wrap.c index 5c02a7ee1..65c70ea28 100644 --- a/src/interface/efi/efi_wrap.c +++ b/src/interface/efi/efi_wrap.c @@ -735,11 +735,14 @@ efi_exit_boot_services_wrapper ( EFI_HANDLE image_handle, UINTN map_key ) { void *retaddr = __builtin_return_address ( 0 ); EFI_STATUS efirc; - DBGC ( colour, "ExitBootServices ( %s, %#llx ) ", + DBGC ( colour, "ExitBootServices ( %s, %#llx ) -> %p\n", efi_handle_name ( image_handle ), - ( ( unsigned long long ) map_key ) ); + ( ( unsigned long long ) map_key ), retaddr ); efirc = bs->ExitBootServices ( image_handle, map_key ); - DBGC ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr ); + if ( efirc != 0 ) { + DBGC ( colour, "ExitBootServices ( ... ) = %s -> %p\n", + efi_status ( efirc ), retaddr ); + } return efirc; } -- cgit v1.2.3-55-g7522 From b6045a8cbb3843f185e8b1ab6488ee6799d0c2b7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 21 Nov 2021 13:27:14 +0000 Subject: [efi] Modify global system table when wrapping a loaded image The EFI loaded image protocol allows an image to be provided with a custom system table, and we currently use this mechanism to wrap any boot services calls made by the loaded image in order to provide strace-like debugging via DEBUG=efi_wrap. The ExitBootServices() call will modify the global system table, leaving the loaded image using a system table that is no longer current. When DEBUG=efi_wrap is used, this generally results in the machine locking up at the point that the loaded operating system calls ExitBootServices(). Fix by modifying the global EFI system table to point to our wrapper functions, instead of providing a custom system table via the loaded image protocol. Signed-off-by: Michael Brown --- src/include/ipxe/efi/efi_wrap.h | 2 +- src/interface/efi/efi_wrap.c | 99 +++++++++++++++++++++++------------------ 2 files changed, 57 insertions(+), 44 deletions(-) diff --git a/src/include/ipxe/efi/efi_wrap.h b/src/include/ipxe/efi/efi_wrap.h index 6c7ccf2e4..2747a9e33 100644 --- a/src/include/ipxe/efi/efi_wrap.h +++ b/src/include/ipxe/efi/efi_wrap.h @@ -10,7 +10,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include -extern EFI_SYSTEM_TABLE * efi_wrap_systab ( void ); +extern EFI_BOOT_SERVICES * efi_wrap_bs ( void ); extern void efi_wrap ( EFI_HANDLE handle ); #endif /* _IPXE_EFI_WRAP_H */ diff --git a/src/interface/efi/efi_wrap.c b/src/interface/efi/efi_wrap.c index 65c70ea28..5d5d2caee 100644 --- a/src/interface/efi/efi_wrap.c +++ b/src/interface/efi/efi_wrap.c @@ -195,6 +195,47 @@ static const char * efi_timer_delay ( EFI_TIMER_DELAY type ) { } } +/** + * Dump information about a loaded image + * + * @v handle Image handle + */ +static void efi_dump_image ( EFI_HANDLE handle ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + EFI_LOADED_IMAGE_PROTOCOL *image; + void *intf; + } loaded; + EFI_STATUS efirc; + int rc; + + /* Open loaded image protocol */ + if ( ( efirc = bs->OpenProtocol ( handle, + &efi_loaded_image_protocol_guid, + &loaded.intf, efi_image_handle, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -EEFI ( efirc ); + DBGC ( colour, "WRAP %s could not get loaded image protocol: " + "%s\n", efi_handle_name ( handle ), strerror ( rc ) ); + return; + } + + /* Dump image information */ + DBGC ( colour, "WRAP %s at base %p has protocols:\n", + efi_handle_name ( handle ), loaded.image->ImageBase ); + DBGC_EFI_PROTOCOLS ( colour, handle ); + DBGC ( colour, "WRAP %s parent", efi_handle_name ( handle ) ); + DBGC ( colour, " %s\n", efi_handle_name ( loaded.image->ParentHandle )); + DBGC ( colour, "WRAP %s device", efi_handle_name ( handle ) ); + DBGC ( colour, " %s\n", efi_handle_name ( loaded.image->DeviceHandle )); + DBGC ( colour, "WRAP %s file", efi_handle_name ( handle ) ); + DBGC ( colour, " %s\n", efi_devpath_text ( loaded.image->FilePath ) ); + + /* Close loaded image protocol */ + bs->CloseProtocol ( handle, &efi_loaded_image_protocol_guid, + efi_image_handle, NULL ); +} + /** * Wrap RaiseTPL() * @@ -655,9 +696,9 @@ efi_load_image_wrapper ( BOOLEAN boot_policy, EFI_HANDLE parent_image_handle, DBGC ( colour, "%s ", efi_handle_name ( *image_handle ) ); DBGC ( colour, ") -> %p\n", retaddr ); - /* Wrap the new image */ + /* Dump information about loaded image */ if ( efirc == 0 ) - efi_wrap ( *image_handle ); + efi_dump_image ( *image_handle ); return efirc; } @@ -1132,12 +1173,11 @@ efi_create_event_ex_wrapper ( UINT32 type, EFI_TPL notify_tpl, } /** - * Build table wrappers + * Build boot services table wrapper * - * @ret systab Wrapped system table + * @ret bs Wrapped boot services table */ -EFI_SYSTEM_TABLE * efi_wrap_systab ( void ) { - static EFI_SYSTEM_TABLE efi_systab_wrapper; +EFI_BOOT_SERVICES * efi_wrap_bs ( void ) { static EFI_BOOT_SERVICES efi_bs_wrapper; EFI_BOOT_SERVICES *bs = efi_systab->BootServices; @@ -1197,12 +1237,7 @@ EFI_SYSTEM_TABLE * efi_wrap_systab ( void ) { = efi_uninstall_multiple_protocol_interfaces_wrapper; efi_bs_wrapper.CreateEventEx = efi_create_event_ex_wrapper; - /* Build system table wrapper */ - memcpy ( &efi_systab_wrapper, efi_systab, - sizeof ( efi_systab_wrapper ) ); - efi_systab_wrapper.BootServices = &efi_bs_wrapper; - - return &efi_systab_wrapper; + return &efi_bs_wrapper; } /** @@ -1211,42 +1246,20 @@ EFI_SYSTEM_TABLE * efi_wrap_systab ( void ) { * @v handle Image handle */ void efi_wrap ( EFI_HANDLE handle ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - union { - EFI_LOADED_IMAGE_PROTOCOL *image; - void *intf; - } loaded; - EFI_STATUS efirc; - int rc; + static EFI_SYSTEM_TABLE efi_systab_copy; /* Do nothing unless debugging is enabled */ if ( ! DBG_LOG ) return; - /* Open loaded image protocol */ - if ( ( efirc = bs->OpenProtocol ( handle, - &efi_loaded_image_protocol_guid, - &loaded.intf, efi_image_handle, NULL, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ - rc = -EEFI ( efirc ); - DBGC ( colour, "WRAP %s could not get loaded image protocol: " - "%s\n", efi_handle_name ( handle ), strerror ( rc ) ); - return; + /* Construct modified system table */ + if ( efi_systab != &efi_systab_copy ) { + memcpy ( &efi_systab_copy, efi_systab, + sizeof ( efi_systab_copy ) ); + efi_systab->BootServices = efi_wrap_bs(); + efi_systab = &efi_systab_copy; } - /* Provide system table wrapper to image */ - loaded.image->SystemTable = efi_wrap_systab(); - DBGC ( colour, "WRAP %s at base %p has protocols:\n", - efi_handle_name ( handle ), loaded.image->ImageBase ); - DBGC_EFI_PROTOCOLS ( colour, handle ); - DBGC ( colour, "WRAP %s parent", efi_handle_name ( handle ) ); - DBGC ( colour, " %s\n", efi_handle_name ( loaded.image->ParentHandle )); - DBGC ( colour, "WRAP %s device", efi_handle_name ( handle ) ); - DBGC ( colour, " %s\n", efi_handle_name ( loaded.image->DeviceHandle )); - DBGC ( colour, "WRAP %s file", efi_handle_name ( handle ) ); - DBGC ( colour, " %s\n", efi_devpath_text ( loaded.image->FilePath ) ); - - /* Close loaded image protocol */ - bs->CloseProtocol ( handle, &efi_loaded_image_protocol_guid, - efi_image_handle, NULL ); + /* Dump image information */ + efi_dump_image ( handle ); } -- cgit v1.2.3-55-g7522 From 3ad27fbe78fec22e9af754242586f2ef621c8d2f Mon Sep 17 00:00:00 2001 From: Benedikt Braunger Date: Tue, 16 Nov 2021 13:42:14 +0100 Subject: [intel] Add PCI ID for Intel X553 0x15e4 Modified-by: Michael Brown Signed-off-by: Michael Brown --- src/drivers/net/intelx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/drivers/net/intelx.c b/src/drivers/net/intelx.c index ccf6b0648..f4dad8859 100644 --- a/src/drivers/net/intelx.c +++ b/src/drivers/net/intelx.c @@ -481,6 +481,7 @@ static struct pci_device_id intelx_nics[] = { PCI_ROM ( 0x8086, 0x15ab, "x552", "X552", 0 ), PCI_ROM ( 0x8086, 0x15c8, "x553t", "X553/X557-AT", 0 ), PCI_ROM ( 0x8086, 0x15ce, "x553-sfp", "X553 (SFP+)", 0 ), + PCI_ROM ( 0x8086, 0x15e4, "x553a", "X553", 0 ), PCI_ROM ( 0x8086, 0x15e5, "x553", "X553", 0 ), }; -- cgit v1.2.3-55-g7522 From a046da21a4c58365be6c029147799db974280048 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 22 Nov 2021 12:50:38 +0000 Subject: [efi] Raise TPL during driver unload entry point The efi_unload() function is currently missing the calls to raise and restore the TPL. This has the side effect of causing iPXE to return from the driver unload entry point at TPL_CALLBACK, which will cause unexpected behaviour (typically a system lockup) shortly afterwards. Fix by adding the missing calls to raise and restore the TPL. Debugged-by: Petr Borsodi Signed-off-by: Michael Brown --- src/interface/efi/efi_init.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c index b7cac16e5..1c6e9d440 100644 --- a/src/interface/efi/efi_init.c +++ b/src/interface/efi/efi_init.c @@ -316,9 +316,13 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle __unused ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_SYSTEM_TABLE *systab = efi_systab; + struct efi_saved_tpl tpl; DBGC ( systab, "EFI image unloading\n" ); + /* Raise TPL */ + efi_raise_tpl ( &tpl ); + /* Shut down */ shutdown_exit(); @@ -336,6 +340,9 @@ static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle __unused ) { DBGC ( systab, "EFI image unloaded\n" ); + /* Restore TPL */ + efi_restore_tpl ( &tpl ); + return 0; } -- cgit v1.2.3-55-g7522 From 0f4cc4b5a7f780111c3ef2e1d4bba175896f8537 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 23 Nov 2021 15:20:09 +0000 Subject: [build] Include EFI system partition table entry in isohybrid images Add the "--uefi" option when invoking isohybrid on an EFI-bootable image, to create a partition mapping to the EFI system partition embedded within the ISO image. This allows the resulting isohybrid image to be booted on UEFI systems that will not recognise an El Torito boot catalog on a non-CDROM device. Originally-fixed-by: Christian Hesse Signed-off-by: Michael Brown --- src/util/genfsimg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/util/genfsimg b/src/util/genfsimg index c13158203..731fa6ce0 100755 --- a/src/util/genfsimg +++ b/src/util/genfsimg @@ -308,6 +308,9 @@ if [ -n "${ISOIMG}" ] ; then ${ISOARGS} "${ISODIR}" if isohybrid --version >/dev/null 2>&1 ; then ISOHYBRIDARGS= + if [ -n "${EFI}" ] ; then + ISOHYBRIDARGS="${ISOHYBRIDARGS} --uefi" + fi if [ -n "${SOURCE_DATE_EPOCH:-}" ] ; then ISOHYBRIDARGS="${ISOHYBRIDARGS} --id ${SOURCE_DATE_EPOCH}" fi -- cgit v1.2.3-55-g7522 From 562c74e1ea52a399f7a6c416e35d98f4b37888cf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 22 Nov 2021 14:53:33 +0000 Subject: [efi] Run ExitBootServices shutdown hook at TPL_NOTIFY On some systems (observed with the Thunderbolt ports on a ThinkPad X1 Extreme Gen3 and a ThinkPad P53), if the IOMMU is enabled then the system firmware will install an ExitBootServices notification event that disables bus mastering on the Thunderbolt xHCI controller and all PCI bridges, and destroys any extant IOMMU mappings. This leaves the xHCI controller unable to perform any DMA operations. As described in commit 236299b ("[xhci] Avoid DMA during shutdown if firmware has disabled bus mastering"), any subsequent DMA operation attempted by the xHCI controller will end up completing after the operating system kernel has reenabled bus mastering, resulting in a DMA operation to an area of memory that the hardware is no longer permitted to access and, on Windows with the Driver Verifier enabled, a STOP 0xE6 (DRIVER_VERIFIER_DMA_VIOLATION). That commit avoids triggering any DMA attempts during the shutdown of the xHCI controller itself. However, this is not a complete solution since any attached and opened USB device (e.g. a USB NIC) may asynchronously trigger DMA attempts that happen to occur after bus mastering has been disabled but before we reset the xHCI controller. Avoid this problem by installing our own ExitBootServices notification event at TPL_NOTIFY, thereby causing it to be invoked before the firmware's own ExitBootServices notification event that disables bus mastering. This unsurprisingly causes the shutdown hook itself to be invoked at TPL_NOTIFY, which causes a fatal error when later code attempts to raise the TPL to TPL_CALLBACK (which is a lower TPL). Work around this problem by redefining the "internal" iPXE TPL to be variable, and set this internal TPL to TPL_NOTIFY when the shutdown hook is invoked. Avoid calling into an underlying SNP protocol instance from within our shutdown hook at TPL_NOTIFY, since the underlying SNP driver may attempt to raise the TPL to TPL_CALLBACK (which would cause a fatal error). Failing to shut down the underlying SNP device is safe to do since the underlying device must, in any case, have installed its own ExitBootServices hook if any shutdown actions are required. Reported-by: Andreas Hammarskjöld Tested-by: Andreas Hammarskjöld Signed-off-by: Michael Brown --- src/drivers/net/efi/nii.c | 2 +- src/drivers/net/efi/snpnet.c | 18 ++++++++++++++---- src/include/ipxe/efi/efi.h | 1 + src/interface/efi/efi_entropy.c | 4 ++-- src/interface/efi/efi_init.c | 20 +++++++++++++++++--- src/interface/efi/efi_timer.c | 2 +- 6 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/drivers/net/efi/nii.c b/src/drivers/net/efi/nii.c index b9f34650e..833462e77 100644 --- a/src/drivers/net/efi/nii.c +++ b/src/drivers/net/efi/nii.c @@ -576,7 +576,7 @@ static int nii_issue_cpb_db ( struct nii_nic *nii, unsigned int op, void *cpb, cdb.IFnum = nii->nii->IfNum; /* Raise task priority level */ - tpl = bs->RaiseTPL ( TPL_CALLBACK ); + tpl = bs->RaiseTPL ( efi_internal_tpl ); /* Issue command */ DBGC2 ( nii, "NII %s issuing %02x:%04x ifnum %d%s%s\n", diff --git a/src/drivers/net/efi/snpnet.c b/src/drivers/net/efi/snpnet.c index fb5240277..69ec6f5e5 100644 --- a/src/drivers/net/efi/snpnet.c +++ b/src/drivers/net/efi/snpnet.c @@ -164,6 +164,10 @@ static int snpnet_transmit ( struct net_device *netdev, EFI_STATUS efirc; int rc; + /* Do nothing if shutdown is in progress */ + if ( efi_shutdown_in_progress ) + return -ECANCELED; + /* Defer the packet if there is already a transmission in progress */ if ( snp->txbuf ) { netdev_tx_defer ( netdev, iobuf ); @@ -283,6 +287,10 @@ static void snpnet_poll_rx ( struct net_device *netdev ) { */ static void snpnet_poll ( struct net_device *netdev ) { + /* Do nothing if shutdown is in progress */ + if ( efi_shutdown_in_progress ) + return; + /* Process any TX completions */ snpnet_poll_tx ( netdev ); @@ -426,8 +434,9 @@ static void snpnet_close ( struct net_device *netdev ) { EFI_STATUS efirc; int rc; - /* Shut down NIC */ - if ( ( efirc = snp->snp->Shutdown ( snp->snp ) ) != 0 ) { + /* Shut down NIC (unless whole system shutdown is in progress) */ + if ( ( ! efi_shutdown_in_progress ) && + ( ( efirc = snp->snp->Shutdown ( snp->snp ) ) != 0 ) ) { rc = -EEFI ( efirc ); DBGC ( snp, "SNP %s could not shut down: %s\n", netdev->name, strerror ( rc ) ); @@ -589,8 +598,9 @@ void snpnet_stop ( struct efi_device *efidev ) { /* Unregister network device */ unregister_netdev ( netdev ); - /* Stop SNP protocol */ - if ( ( efirc = snp->snp->Stop ( snp->snp ) ) != 0 ) { + /* Stop SNP protocol (unless whole system shutdown is in progress) */ + if ( ( ! efi_shutdown_in_progress ) && + ( ( efirc = snp->snp->Stop ( snp->snp ) ) != 0 ) ) { rc = -EEFI ( efirc ); DBGC ( device, "SNP %s could not stop: %s\n", efi_handle_name ( device ), strerror ( rc ) ); diff --git a/src/include/ipxe/efi/efi.h b/src/include/ipxe/efi/efi.h index a83fa0f27..1dd0d4453 100644 --- a/src/include/ipxe/efi/efi.h +++ b/src/include/ipxe/efi/efi.h @@ -223,6 +223,7 @@ extern EFI_HANDLE efi_image_handle; extern EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image; extern EFI_DEVICE_PATH_PROTOCOL *efi_loaded_image_path; extern EFI_SYSTEM_TABLE *efi_systab; +extern EFI_TPL efi_internal_tpl; extern EFI_TPL efi_external_tpl; extern int efi_shutdown_in_progress; diff --git a/src/interface/efi/efi_entropy.c b/src/interface/efi/efi_entropy.c index 70cd06293..1e8ddfb68 100644 --- a/src/interface/efi/efi_entropy.c +++ b/src/interface/efi/efi_entropy.c @@ -104,8 +104,8 @@ static void efi_entropy_disable ( void ) { /* Close timer tick event */ bs->CloseEvent ( tick ); - /* Return to TPL_CALLBACK */ - bs->RaiseTPL ( TPL_CALLBACK ); + /* Return to internal TPL */ + bs->RaiseTPL ( efi_internal_tpl ); } /** diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c index 1c6e9d440..5d98f9ff7 100644 --- a/src/interface/efi/efi_init.c +++ b/src/interface/efi/efi_init.c @@ -47,6 +47,9 @@ EFI_DEVICE_PATH_PROTOCOL *efi_loaded_image_path; */ EFI_SYSTEM_TABLE * _C2 ( PLATFORM, _systab ); +/** Internal task priority level */ +EFI_TPL efi_internal_tpl = TPL_CALLBACK; + /** External task priority level */ EFI_TPL efi_external_tpl = TPL_APPLICATION; @@ -79,6 +82,17 @@ static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle ); static EFIAPI void efi_shutdown_hook ( EFI_EVENT event __unused, void *context __unused ) { + /* This callback is invoked at TPL_NOTIFY in order to ensure + * that we have an opportunity to shut down cleanly before + * other shutdown hooks perform destructive operations such as + * disabling the IOMMU. + * + * Modify the internal task priority level so that no code + * attempts to raise from TPL_NOTIFY to TPL_CALLBACK (which + * would trigger a fatal exception). + */ + efi_internal_tpl = TPL_NOTIFY; + /* Mark shutdown as being in progress, to indicate that large * parts of the system (e.g. timers) are no longer functional. */ @@ -273,7 +287,7 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, * bother doing so when ExitBootServices() is called. */ if ( ( efirc = bs->CreateEvent ( EVT_SIGNAL_EXIT_BOOT_SERVICES, - TPL_CALLBACK, efi_shutdown_hook, + TPL_NOTIFY, efi_shutdown_hook, NULL, &efi_shutdown_event ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( systab, "EFI could not create ExitBootServices event: " @@ -373,7 +387,7 @@ __attribute__ (( noreturn )) void __stack_chk_fail ( void ) { } /** - * Raise task priority level to TPL_CALLBACK + * Raise task priority level to internal level * * @v tpl Saved TPL */ @@ -384,7 +398,7 @@ void efi_raise_tpl ( struct efi_saved_tpl *tpl ) { tpl->previous = efi_external_tpl; /* Raise TPL and record previous TPL as new external TPL */ - tpl->current = bs->RaiseTPL ( TPL_CALLBACK ); + tpl->current = bs->RaiseTPL ( efi_internal_tpl ); efi_external_tpl = tpl->current; } diff --git a/src/interface/efi/efi_timer.c b/src/interface/efi/efi_timer.c index 405cd3454..6427eb1d8 100644 --- a/src/interface/efi/efi_timer.c +++ b/src/interface/efi/efi_timer.c @@ -137,7 +137,7 @@ static unsigned long efi_currticks ( void ) { efi_jiffies++; } else { bs->RestoreTPL ( efi_external_tpl ); - bs->RaiseTPL ( TPL_CALLBACK ); + bs->RaiseTPL ( efi_internal_tpl ); } return ( efi_jiffies * ( TICKS_PER_SEC / EFI_JIFFIES_PER_SEC ) ); -- cgit v1.2.3-55-g7522 From 9062544f6a0c69c249b90d21a08d05518aafc2ec Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 24 Nov 2021 15:43:46 +0000 Subject: [efi] Disable EFI watchdog timer when shutting down to boot an OS The UEFI specification mandates that the EFI watchdog timer should be disabled by the platform firmware as part of the ExitBootServices() call, but some platforms (e.g. Hyper-V) are observed to occasionally forget to do so, resulting in a reboot approximately five minutes after starting the operating system. Work around these firmware bugs by disabling the watchdog timer ourselves. Requested-by: Andreas Hammarskjöld Tested-by: Andreas Hammarskjöld Signed-off-by: Michael Brown --- src/interface/efi/efi_watchdog.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/interface/efi/efi_watchdog.c b/src/interface/efi/efi_watchdog.c index 7061f81d8..dcc9a5668 100644 --- a/src/interface/efi/efi_watchdog.c +++ b/src/interface/efi/efi_watchdog.c @@ -34,6 +34,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include @@ -80,3 +81,36 @@ static void efi_watchdog_expired ( struct retry_timer *timer, /** Watchdog holdoff timer */ struct retry_timer efi_watchdog = TIMER_INIT ( efi_watchdog_expired ); + +/** + * Disable watching when shutting down to boot an operating system + * + * @v booting System is shutting down for OS boot + */ +static void efi_watchdog_shutdown ( int booting ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + int rc; + + /* If we are shutting down to boot an operating system, then + * disable the boot services watchdog timer. The UEFI + * specification mandates that the platform firmware does this + * as part of the ExitBootServices() call, but some platforms + * (e.g. Hyper-V) are observed to occasionally forget to do + * so, resulting in a reboot approximately five minutes after + * starting the operating system. + */ + if ( booting && + ( ( efirc = bs->SetWatchdogTimer ( 0, 0, 0, NULL ) ) != 0 ) ) { + rc = -EEFI ( efirc ); + DBGC ( &efi_watchdog, "EFI could not disable watchdog timer: " + "%s\n", strerror ( rc ) ); + /* Nothing we can do */ + } +} + +/** Watchdog startup/shutdown function */ +struct startup_fn efi_watchdog_startup_fn __startup_fn ( STARTUP_EARLY ) = { + .name = "efi_watchdog", + .shutdown = efi_watchdog_shutdown, +}; -- cgit v1.2.3-55-g7522 From f43c2fd69749bb9a44f2a3ab61b6735938432b52 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 4 Jan 2022 13:31:15 +0000 Subject: [settings] Support formatting UUIDs as little-endian GUIDs The RFC4122 specification defines UUIDs as being in network byte order, but an unfortunately significant amount of (mostly Microsoft) software treats them as having the first three fields in little-endian byte order. In an ideal world, any server-side software that compares UUIDs for equality would perform an endian-insensitive comparison (analogous to comparing strings for equality using a case-insensitive comparison), and would therefore not care about byte order differences. Define a setting type name ":guid" to allow a UUID setting to be formatted in little-endian order, to simplify interoperability with server-side software that expects such a formatting. Signed-off-by: Michael Brown --- src/core/settings.c | 23 ++++++++++++++++++----- src/include/ipxe/settings.h | 1 + src/interface/smbios/smbios_settings.c | 3 ++- src/tests/settings_test.c | 10 ++++++++++ 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/core/settings.c b/src/core/settings.c index 430cdc84b..fcdf98d2b 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -2199,7 +2199,7 @@ const struct setting_type setting_type_base64 __setting_type = { }; /** - * Format UUID setting value + * Format UUID/GUID setting value * * @v type Setting type * @v raw Raw setting value @@ -2208,17 +2208,24 @@ const struct setting_type setting_type_base64 __setting_type = { * @v len Length of buffer * @ret len Length of formatted value, or negative error */ -static int format_uuid_setting ( const struct setting_type *type __unused, +static int format_uuid_setting ( const struct setting_type *type, const void *raw, size_t raw_len, char *buf, size_t len ) { - const union uuid *uuid = raw; + union uuid uuid; /* Range check */ - if ( raw_len != sizeof ( *uuid ) ) + if ( raw_len != sizeof ( uuid ) ) return -ERANGE; + /* Copy value */ + memcpy ( &uuid, raw, sizeof ( uuid ) ); + + /* Mangle GUID byte ordering */ + if ( type == &setting_type_guid ) + uuid_mangle ( &uuid ); + /* Format value */ - return snprintf ( buf, len, "%s", uuid_ntoa ( uuid ) ); + return snprintf ( buf, len, "%s", uuid_ntoa ( &uuid ) ); } /** UUID setting type */ @@ -2227,6 +2234,12 @@ const struct setting_type setting_type_uuid __setting_type = { .format = format_uuid_setting, }; +/** GUID setting type */ +const struct setting_type setting_type_guid __setting_type = { + .name = "guid", + .format = format_uuid_setting, +}; + /** * Format PCI bus:dev.fn setting value * diff --git a/src/include/ipxe/settings.h b/src/include/ipxe/settings.h index f463e6674..e042b9758 100644 --- a/src/include/ipxe/settings.h +++ b/src/include/ipxe/settings.h @@ -426,6 +426,7 @@ extern const struct setting_type setting_type_hexhyp __setting_type; extern const struct setting_type setting_type_hexraw __setting_type; extern const struct setting_type setting_type_base64 __setting_type; extern const struct setting_type setting_type_uuid __setting_type; +extern const struct setting_type setting_type_guid __setting_type; extern const struct setting_type setting_type_busdevfn __setting_type; extern const struct setting_type setting_type_dnssl __setting_type; diff --git a/src/interface/smbios/smbios_settings.c b/src/interface/smbios/smbios_settings.c index 2d571f2e4..ec31b43f2 100644 --- a/src/interface/smbios/smbios_settings.c +++ b/src/interface/smbios/smbios_settings.c @@ -140,7 +140,8 @@ static int smbios_fetch ( struct settings *settings __unused, * is 2.6 or higher; we match this behaviour. */ raw = &buf[tag_offset]; - if ( ( setting->type == &setting_type_uuid ) && + if ( ( ( setting->type == &setting_type_uuid ) || + ( setting->type == &setting_type_guid ) ) && ( tag_len == sizeof ( uuid ) ) && ( smbios_version() >= SMBIOS_VERSION ( 2, 6 ) ) ) { DBG ( "SMBIOS detected mangled UUID\n" ); diff --git a/src/tests/settings_test.c b/src/tests/settings_test.c index 828901b06..5da7eb008 100644 --- a/src/tests/settings_test.c +++ b/src/tests/settings_test.c @@ -250,6 +250,12 @@ static struct setting test_uuid_setting = { .type = &setting_type_uuid, }; +/** Test GUID setting type */ +static struct setting test_guid_setting = { + .name = "test_guid", + .type = &setting_type_guid, +}; + /** Test PCI bus:dev.fn setting type */ static struct setting test_busdevfn_setting = { .name = "test_busdevfn", @@ -419,6 +425,10 @@ static void settings_test_exec ( void ) { RAW ( 0x1a, 0x6a, 0x74, 0x9d, 0x0e, 0xda, 0x46, 0x1a,0xa8, 0x7a, 0x7c, 0xfe, 0x4f, 0xca, 0x4a, 0x57 ), "1a6a749d-0eda-461a-a87a-7cfe4fca4a57" ); + fetchf_ok ( &test_settings, &test_guid_setting, + RAW ( 0x1a, 0x6a, 0x74, 0x9d, 0x0e, 0xda, 0x46, 0x1a,0xa8, + 0x7a, 0x7c, 0xfe, 0x4f, 0xca, 0x4a, 0x57 ), + "9d746a1a-da0e-1a46-a87a-7cfe4fca4a57" ); /* "busdevfn" setting type (no store capability) */ fetchf_ok ( &test_settings, &test_busdevfn_setting, -- cgit v1.2.3-55-g7522 From 91c77e2592f5b0e1098d3f341827d7e6925e4a85 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 11 Jan 2022 15:27:14 +0000 Subject: [efi] Do not align VirtualSize for .reloc and .debug sections As of commit f1e9e2b ("[efi] Align EFI image sections by page size"), the VirtualSize fields for the .reloc and .debug sections have been rounded up to the (4kB) image alignment. This breaks the PE relocation logic in the UEFI shim, which requires the VirtualSize field to exactly match the size as recorded in the data directory. Fix by setting the VirtualSize field to the unaligned size of the section, as is already done for normal PE sections (i.e. those other than .reloc and .debug). Signed-off-by: Michael Brown --- src/util/elf2efi.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/util/elf2efi.c b/src/util/elf2efi.c index 8af53aeb4..08d790f1e 100644 --- a/src/util/elf2efi.c +++ b/src/util/elf2efi.c @@ -753,15 +753,13 @@ static struct pe_section * create_reloc_section ( struct pe_header *pe_header, struct pe_relocs *pe_reltab ) { struct pe_section *reloc; - size_t section_rawsz; size_t section_memsz; size_t section_filesz; EFI_IMAGE_DATA_DIRECTORY *relocdir; /* Allocate PE section */ - section_rawsz = output_pe_reltab ( pe_reltab, NULL ); - section_filesz = efi_file_align ( section_rawsz ); - section_memsz = efi_image_align ( section_rawsz ); + section_memsz = output_pe_reltab ( pe_reltab, NULL ); + section_filesz = efi_file_align ( section_memsz ); reloc = xmalloc ( sizeof ( *reloc ) + section_filesz ); memset ( reloc, 0, sizeof ( *reloc ) + section_filesz ); @@ -782,11 +780,12 @@ create_reloc_section ( struct pe_header *pe_header, /* Update file header details */ pe_header->nt.FileHeader.NumberOfSections++; pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( reloc->hdr ); - pe_header->nt.OptionalHeader.SizeOfImage += section_memsz; + pe_header->nt.OptionalHeader.SizeOfImage += + efi_image_align ( section_memsz ); relocdir = &(pe_header->nt.OptionalHeader.DataDirectory [EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC]); relocdir->VirtualAddress = reloc->hdr.VirtualAddress; - relocdir->Size = section_rawsz; + relocdir->Size = section_memsz; return reloc; } @@ -824,8 +823,8 @@ create_debug_section ( struct pe_header *pe_header, const char *filename ) { } *contents; /* Allocate PE section */ - section_memsz = efi_image_align ( sizeof ( *contents ) ); - section_filesz = efi_file_align ( sizeof ( *contents ) ); + section_memsz = sizeof ( *contents ); + section_filesz = efi_file_align ( section_memsz ); debug = xmalloc ( sizeof ( *debug ) + section_filesz ); memset ( debug, 0, sizeof ( *debug ) + section_filesz ); contents = ( void * ) debug->contents; @@ -857,7 +856,8 @@ create_debug_section ( struct pe_header *pe_header, const char *filename ) { /* Update file header details */ pe_header->nt.FileHeader.NumberOfSections++; pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( debug->hdr ); - pe_header->nt.OptionalHeader.SizeOfImage += section_memsz; + pe_header->nt.OptionalHeader.SizeOfImage += + efi_image_align ( section_memsz ); debugdir = &(pe_header->nt.OptionalHeader.DataDirectory [EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]); debugdir->VirtualAddress = debug->hdr.VirtualAddress; -- cgit v1.2.3-55-g7522 From 53a5de3641ccd0836aac7378cdd37c9757e2db3a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 13 Jan 2022 12:48:38 +0000 Subject: [doc] Update user-visible ipxe.org URIs to use HTTPS Signed-off-by: Michael Brown --- src/Makefile | 2 +- src/arch/x86/interface/syslinux/comboot_call.c | 2 +- src/arch/x86/prefix/romprefix.S | 4 ++-- src/config/branding.h | 24 ++++++++++++------------ src/include/ipxe/srp.h | 2 +- src/usr/autoboot.c | 4 ++-- src/util/niclist.pl | 2 +- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Makefile b/src/Makefile index 69139dc11..4c4abf1aa 100644 --- a/src/Makefile +++ b/src/Makefile @@ -190,7 +190,7 @@ vmware : bin/8086100f.mrom bin/808610d3.mrom bin/10222000.rom bin/15ad07b0.rom @$(ECHO) ' bin/10222000.rom -- vlance/pcnet32' @$(ECHO) ' bin/15ad07b0.rom -- vmxnet3' @$(ECHO) - @$(ECHO) 'For more information, see http://ipxe.org/howto/vmware' + @$(ECHO) 'For more information, see https://ipxe.org/howto/vmware' @$(ECHO) @$(ECHO) '===========================================================' diff --git a/src/arch/x86/interface/syslinux/comboot_call.c b/src/arch/x86/interface/syslinux/comboot_call.c index dc308dafe..b75e8ef7c 100644 --- a/src/arch/x86/interface/syslinux/comboot_call.c +++ b/src/arch/x86/interface/syslinux/comboot_call.c @@ -47,7 +47,7 @@ static char __bss16_array ( syslinux_version, [32] ); #define syslinux_version __use_data16 ( syslinux_version ) /** The "SYSLINUX" copyright string */ -static char __data16_array ( syslinux_copyright, [] ) = " http://ipxe.org"; +static char __data16_array ( syslinux_copyright, [] ) = " https://ipxe.org"; #define syslinux_copyright __use_data16 ( syslinux_copyright ) static char __data16_array ( syslinux_configuration_file, [] ) = ""; diff --git a/src/arch/x86/prefix/romprefix.S b/src/arch/x86/prefix/romprefix.S index a9934a725..4e8793c21 100644 --- a/src/arch/x86/prefix/romprefix.S +++ b/src/arch/x86/prefix/romprefix.S @@ -161,7 +161,7 @@ pnpheader: /* Manufacturer string */ mfgstr: - .asciz "http://ipxe.org" + .asciz "https://ipxe.org" .size mfgstr, . - mfgstr /* Product string @@ -607,7 +607,7 @@ get_pmm_decompress_to: * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/branding.h. * * While nothing in the GPL prevents you from removing all references - * to iPXE or http://ipxe.org, we prefer you not to do so. + * to iPXE or https://ipxe.org, we prefer you not to do so. * * If you have an OEM-mandated branding requirement that cannot be * satisfied simply by defining PRODUCT_NAME and PRODUCT_SHORT_NAME, diff --git a/src/config/branding.h b/src/config/branding.h index 73f00af95..e503dff9a 100644 --- a/src/config/branding.h +++ b/src/config/branding.h @@ -26,7 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define PRODUCT_NAME "" #define PRODUCT_SHORT_NAME "iPXE" -#define PRODUCT_URI "http://ipxe.org" +#define PRODUCT_URI "https://ipxe.org" /* * Tag line @@ -44,15 +44,15 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * (e.g. "Permission denied") and a 32-bit error number. This number * is incorporated into an error URI such as * - * "No such file or directory (http://ipxe.org/2d0c613b)" + * "No such file or directory (https://ipxe.org/2d0c613b)" * * or * - * "Operation not supported (http://ipxe.org/3c092003)" + * "Operation not supported (https://ipxe.org/3c092003)" * * Users may browse to the URI within the error message, which is * provided by a database running on the iPXE web site - * (http://ipxe.org). This database provides details for all possible + * (https://ipxe.org). This database provides details for all possible * errors generated by iPXE, including: * * - the detailed error message (e.g. "Not an OCSP signing @@ -74,13 +74,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * * If you have a customer support team and would like your customers * to contact your support team for all problems, instead of using the - * existing support infrastructure provided by http://ipxe.org, then + * existing support infrastructure provided by https://ipxe.org, then * you may define a custom URI to be included within error messages. * * Note that the custom URI is a printf() format string which must * include a format specifier for the 32-bit error number. */ -#define PRODUCT_ERROR_URI "http://ipxe.org/%08x" +#define PRODUCT_ERROR_URI "https://ipxe.org/%08x" /* * Command help messages @@ -88,7 +88,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * iPXE command help messages include a URI constructed from the * command name, such as * - * "See http://ipxe.org/cmd/vcreate for further information" + * "See https://ipxe.org/cmd/vcreate for further information" * * The iPXE web site includes documentation for the commands provided * by the iPXE shell, including: @@ -113,7 +113,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * * If you want to provide your own documentation for all of the * commands provided by the iPXE shell, rather than using the existing - * support infrastructure provided by http://ipxe.org, then you may + * support infrastructure provided by https://ipxe.org, then you may * define a custom URI to be included within command help messages. * * Note that the custom URI is a printf() format string which must @@ -124,7 +124,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * iPXE project and prohibit the alteration or removal of any * references to "iPXE". ] */ -#define PRODUCT_COMMAND_URI "http://ipxe.org/cmd/%s" +#define PRODUCT_COMMAND_URI "https://ipxe.org/cmd/%s" /* * Setting help messages @@ -132,7 +132,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * iPXE setting help messages include a URI constructed from the * setting name, such as * - * "http://ipxe.org/cfg/initiator-iqn" + * "https://ipxe.org/cfg/initiator-iqn" * * The iPXE web site includes documentation for the settings used by * iPXE, including: @@ -156,7 +156,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * * If you want to provide your own documentation for all of the * settings used by iPXE, rather than using the existing support - * infrastructure provided by http://ipxe.org, then you may define a + * infrastructure provided by https://ipxe.org, then you may define a * custom URI to be included within setting help messages. * * Note that the custom URI is a printf() format string which must @@ -167,7 +167,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * iPXE project and prohibit the alteration or removal of any * references to "iPXE". ] */ -#define PRODUCT_SETTING_URI "http://ipxe.org/cfg/%s" +#define PRODUCT_SETTING_URI "https://ipxe.org/cfg/%s" #include diff --git a/src/include/ipxe/srp.h b/src/include/ipxe/srp.h index 3abb0995f..1f66a22b2 100644 --- a/src/include/ipxe/srp.h +++ b/src/include/ipxe/srp.h @@ -774,7 +774,7 @@ struct srp_aer_rsp { * The working draft specification for the SRP boot firmware table can * be found at * - * http://ipxe.org/wiki/srp/sbft + * https://ipxe.org/wiki/srp/sbft * ***************************************************************************** */ diff --git a/src/usr/autoboot.c b/src/usr/autoboot.c index 62e90ecd0..24043ae69 100644 --- a/src/usr/autoboot.c +++ b/src/usr/autoboot.c @@ -579,8 +579,8 @@ int ipxe ( struct net_device *netdev ) { * defining the string PRODUCT_NAME in config/branding.h. * * While nothing in the GPL prevents you from removing all - * references to iPXE or http://ipxe.org, we prefer you not to - * do so. + * references to iPXE or https://ipxe.org, we prefer you not + * to do so. * */ printf ( NORMAL "\n\n" PRODUCT_NAME "\n" BOLD PRODUCT_SHORT_NAME " %s" diff --git a/src/util/niclist.pl b/src/util/niclist.pl index 2668a1c03..c35a3277e 100755 --- a/src/util/niclist.pl +++ b/src/util/niclist.pl @@ -565,7 +565,7 @@ EOM return join("\n", @output); } -# Output NIC list in DokuWiki format (for http://ipxe.org) +# Output NIC list in DokuWiki format (for https://ipxe.org) sub format_nic_list_dokuwiki { my ($nic_list, $column_names) = @_; my @output; -- cgit v1.2.3-55-g7522 From fbbdc39260cf37aa749e897e773f59807d1b8362 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 13 Jan 2022 13:43:08 +0000 Subject: [build] Ensure version.%.o is always rebuilt as expected Signed-off-by: Michael Brown --- src/Makefile.housekeeping | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index 0deb15c10..24ed5d13b 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -918,7 +918,7 @@ $(BIN)/deps/%.d : % $(MAKEDEPS) # Calculate list of dependency files # -AUTO_DEPS = $(patsubst %,$(BIN)/deps/%.d,$(AUTO_SRCS)) +AUTO_DEPS = $(patsubst %,$(BIN)/deps/%.d,$(AUTO_SRCS) core/version.c) autodeps : @$(ECHO) $(AUTO_DEPS) VERYCLEANUP += $(BIN)/deps @@ -1202,7 +1202,7 @@ endif # Build version # GIT_INDEX := $(if $(GITVERSION),$(if $(wildcard ../.git/index),../.git/index)) -$(BIN)/version.%.o : core/version.c $(MAKEDEPS) $(GIT_INDEX) +$(BIN)/version.%.o : core/version.c $(MAKEDEPS) $(version_DEPS) $(GIT_INDEX) $(QM)$(ECHO) " [VERSION] $@" $(Q)$(COMPILE_c) -DBUILD_NAME="\"$*\"" \ -DVERSION_MAJOR=$(VERSION_MAJOR) \ -- cgit v1.2.3-55-g7522 From f4f9adf618cd85d330a896e1f721f3aa78d2409d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 13 Jan 2022 14:10:03 +0000 Subject: [efi] Include Secure Boot Advanced Targeting (SBAT) metadata SBAT defines an encoding for security generation numbers stored as a CSV file within a special ".sbat" section in the signed binary. If a Secure Boot exploit is discovered then the generation number will be incremented alongside the corresponding fix. Platforms may then record the minimum generation number required for any given product. This allows for an efficient revocation mechanism that consumes minimal flash storage space (in contrast to the DBX mechanism, which allows for only a single-digit number of revocation events to ever take place across all possible signed binaries). Add SBAT metadata to iPXE EFI binaries to support this mechanism. Signed-off-by: Michael Brown --- src/arch/i386/scripts/i386-kir.lds | 2 ++ src/arch/i386/scripts/linux.lds | 2 ++ src/arch/x86/scripts/pcbios.lds | 2 ++ src/arch/x86/scripts/prefixonly.lds | 2 ++ src/arch/x86_64/scripts/linux.lds | 2 ++ src/config/branding.h | 18 ++++++++++ src/core/version.c | 30 ++++++++++++++++ src/include/ipxe/sbat.h | 68 +++++++++++++++++++++++++++++++++++++ src/scripts/efi.lds | 13 +++++++ 9 files changed, 139 insertions(+) create mode 100644 src/include/ipxe/sbat.h diff --git a/src/arch/i386/scripts/i386-kir.lds b/src/arch/i386/scripts/i386-kir.lds index 66bf804e6..13c36f2bf 100644 --- a/src/arch/i386/scripts/i386-kir.lds +++ b/src/arch/i386/scripts/i386-kir.lds @@ -136,6 +136,8 @@ SECTIONS { *(.note.*) *(.discard) *(.discard.*) + *(.sbat) + *(.sbat.*) } /* diff --git a/src/arch/i386/scripts/linux.lds b/src/arch/i386/scripts/linux.lds index 9f2eeaf3c..8c3a7b0ba 100644 --- a/src/arch/i386/scripts/linux.lds +++ b/src/arch/i386/scripts/linux.lds @@ -100,5 +100,7 @@ SECTIONS { *(.rel.*) *(.discard) *(.discard.*) + *(.sbat) + *(.sbat.*) } } diff --git a/src/arch/x86/scripts/pcbios.lds b/src/arch/x86/scripts/pcbios.lds index de59adca9..e208b174b 100644 --- a/src/arch/x86/scripts/pcbios.lds +++ b/src/arch/x86/scripts/pcbios.lds @@ -229,6 +229,8 @@ SECTIONS { *(.einfo.*) *(.discard) *(.discard.*) + *(.sbat) + *(.sbat.*) } /* diff --git a/src/arch/x86/scripts/prefixonly.lds b/src/arch/x86/scripts/prefixonly.lds index dce0930b5..2fe5b03be 100644 --- a/src/arch/x86/scripts/prefixonly.lds +++ b/src/arch/x86/scripts/prefixonly.lds @@ -24,6 +24,8 @@ SECTIONS { *(.einfo.*) *(.discard) *(.discard.*) + *(.sbat) + *(.sbat.*) } } diff --git a/src/arch/x86_64/scripts/linux.lds b/src/arch/x86_64/scripts/linux.lds index 47db21745..a093787e5 100644 --- a/src/arch/x86_64/scripts/linux.lds +++ b/src/arch/x86_64/scripts/linux.lds @@ -100,5 +100,7 @@ SECTIONS { *(.rel.*) *(.discard) *(.discard.*) + *(.sbat) + *(.sbat.*) } } diff --git a/src/config/branding.h b/src/config/branding.h index e503dff9a..454bf0c03 100644 --- a/src/config/branding.h +++ b/src/config/branding.h @@ -169,6 +169,24 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define PRODUCT_SETTING_URI "https://ipxe.org/cfg/%s" +/* + * Product security name suffix + * + * Vendors creating signed iPXE binaries must set this to a non-empty + * value (e.g. "2pint"). + */ +#define PRODUCT_SBAT_NAME "" + +/* + * Product security generation + * + * Vendors creating signed iPXE binaries must set this to a non-zero + * value, and must increment the value whenever a Secure Boot exploit + * is fixed (unless the upstream IPXE_SBAT_GENERATION has already been + * incremented as part of that fix). + */ +#define PRODUCT_SBAT_GENERATION 0 + #include #endif /* CONFIG_BRANDING_H */ diff --git a/src/core/version.c b/src/core/version.c index c984335c2..22f444065 100644 --- a/src/core/version.c +++ b/src/core/version.c @@ -32,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include @@ -92,3 +93,32 @@ const wchar_t build_wname[] = WSTRING ( BUILD_NAME ); /** Copy of build name string within ".prefix" */ const char build_name_prefix[] __attribute__ (( section ( ".prefix.name" ) )) = BUILD_NAME; + +/** SBAT upstream iPXE line + * + * This line represents the security generation of the upstream + * codebase from which this build is derived. + */ +#define SBAT_IPXE \ + SBAT_LINE ( "ipxe", IPXE_SBAT_GENERATION, \ + "iPXE", BUILD_NAME, VERSION, "https://ipxe.org" ) + +/** SBAT local build line + * + * This line states the security generation of the local build, which + * may include non-default features or non-upstreamed modifications. + */ +#if PRODUCT_SBAT_GENERATION +#define SBAT_PRODUCT \ + SBAT_LINE ( "ipxe." PRODUCT_SBAT_NAME, PRODUCT_SBAT_GENERATION, \ + PRODUCT_SHORT_NAME, BUILD_NAME, VERSION, \ + PRODUCT_URI ) +#else +#define SBAT_PRODUCT "" +#endif + +/** SBAT data */ +#define SBAT_DATA SBAT_HEADER "" SBAT_IPXE "" SBAT_PRODUCT + +/** SBAT data (without any NUL terminator) */ +const char sbat[ sizeof ( SBAT_DATA ) - 1 ] __sbat = SBAT_DATA; diff --git a/src/include/ipxe/sbat.h b/src/include/ipxe/sbat.h new file mode 100644 index 000000000..4b74670ed --- /dev/null +++ b/src/include/ipxe/sbat.h @@ -0,0 +1,68 @@ +#ifndef _IPXE_SBAT_H +#define _IPXE_SBAT_H + +/** @file + * + * Secure Boot Advanced Targeting (SBAT) + * + * SBAT defines an encoding for security generation numbers stored as + * a CSV file within a special ".sbat" section in the signed binary. + * If a Secure Boot exploit is discovered then the generation number + * will be incremented alongside the corresponding fix. + * + * Platforms may then record the minimum generation number required + * for any given product. This allows for an efficient revocation + * mechanism that consumes minimal flash storage space (in contrast to + * the DBX mechanism, which allows for only a single-digit number of + * revocation events to ever take place across all possible signed + * binaries). + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * A single line within an SBAT CSV file + * + * @v name Machine-readable component name + * @v generation Security generation number + * @v vendor Human-readable vendor name + * @v package Human-readable package name + * @v version Human-readable package version + * @v uri Contact URI + * @ret line CSV line + */ +#define SBAT_LINE( name, generation, vendor, package, version, uri ) \ + name "," _S2 ( generation ) "," vendor "," package "," \ + version "," uri "\n" + +/** SBAT format generation */ +#define SBAT_GENERATION 1 + +/** Upstream security generation + * + * This represents the security generation of the upstream codebase. + * It will be incremented whenever a Secure Boot exploit is fixed in + * the upstream codebase. + * + * If you do not have commit access to the upstream iPXE repository, + * then you may not modify this value under any circumstances. + */ +#define IPXE_SBAT_GENERATION 1 + +/* Seriously, do not modify this value */ +#if IPXE_SBAT_GENERATION != 1 +#error "You may not modify IPXE_SBAT_GENERATION" +#endif + +/** SBAT header line */ +#define SBAT_HEADER \ + SBAT_LINE ( "sbat", SBAT_GENERATION, "SBAT Version", "sbat", \ + _S2 ( SBAT_GENERATION ), \ + "https://github.com/rhboot/shim/blob/main/SBAT.md" ) + +/** Mark variable as being in the ".sbat" section */ +#define __sbat __attribute__ (( section ( ".sbat" ), aligned ( 512 ) )) + +extern const char sbat[] __sbat; + +#endif /* _IPXE_SBAT_H */ diff --git a/src/scripts/efi.lds b/src/scripts/efi.lds index dd7b3f019..218b1df66 100644 --- a/src/scripts/efi.lds +++ b/src/scripts/efi.lds @@ -74,6 +74,19 @@ SECTIONS { _ebss = .; } + /* + * The SBAT section + * + */ + + . = ALIGN ( _page_align ); + .sbat : { + _sbat = .; + KEEP(*(.sbat)) + KEEP(*(.sbat.*)) + _esbat = .; + } + /* * Weak symbols that need zero values if not otherwise defined * -- cgit v1.2.3-55-g7522 From e814d33900992e034a8c3ddec2c65463c5206090 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 13 Jan 2022 14:53:36 +0000 Subject: [uri] Allow for relative URIs that include colons within the path RFC3986 allows for colons to appear within the path component of a relative URI, but iPXE will currently parse such URIs incorrectly by interpreting the text before the colon as the URI scheme. Fix by checking for valid characters when identifying the URI scheme. Deliberately deviate from the RFC3986 definition of valid characters by accepting "_" (which was incorrectly used in the iPXE-specific "ib_srp" URI scheme and so must be accepted for compatibility with existing deployments), and by omitting the code to check for characters that are not used in any URI scheme supported by iPXE. Reported-by: Ignat Korchagin Signed-off-by: Michael Brown --- src/core/uri.c | 15 ++++++++++----- src/tests/uri_test.c | 10 ++++++++++ 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/core/uri.c b/src/core/uri.c index a0f79e9ec..b82472ef0 100644 --- a/src/core/uri.c +++ b/src/core/uri.c @@ -334,8 +334,15 @@ struct uri * parse_uri ( const char *uri_string ) { uri->efragment = tmp; } - /* Identify absolute/relative URI */ - if ( ( tmp = strchr ( raw, ':' ) ) ) { + /* Identify absolute URIs */ + epath = raw; + for ( tmp = raw ; ; tmp++ ) { + /* Possible scheme character (for our URI schemes) */ + if ( isalpha ( *tmp ) || ( *tmp == '-' ) || ( *tmp == '_' ) ) + continue; + /* Invalid scheme character or NUL: is a relative URI */ + if ( *tmp != ':' ) + break; /* Absolute URI: identify hierarchical/opaque */ uri->scheme = raw; *(tmp++) = '\0'; @@ -347,9 +354,7 @@ struct uri * parse_uri ( const char *uri_string ) { uri->opaque = tmp; epath = NULL; } - } else { - /* Relative URI */ - epath = raw; + break; } /* If we don't have a path (i.e. we have an absolute URI with diff --git a/src/tests/uri_test.c b/src/tests/uri_test.c index 929ab3632..338f479cd 100644 --- a/src/tests/uri_test.c +++ b/src/tests/uri_test.c @@ -657,6 +657,15 @@ static struct uri_test uri_file_volume = { }, }; +/** Relative URI with colons in path */ +static struct uri_test uri_colons = { + "/boot/52:54:00:12:34:56/boot.ipxe", + { + .path = "/boot/52:54:00:12:34:56/boot.ipxe", + .epath = "/boot/52:54:00:12:34:56/boot.ipxe", + }, +}; + /** URI with port number */ static struct uri_port_test uri_explicit_port = { "http://192.168.0.1:8080/boot.php", @@ -957,6 +966,7 @@ static void uri_test_exec ( void ) { uri_parse_format_dup_ok ( &uri_file_relative ); uri_parse_format_dup_ok ( &uri_file_absolute ); uri_parse_format_dup_ok ( &uri_file_volume ); + uri_parse_format_dup_ok ( &uri_colons ); /** URI port number tests */ uri_port_ok ( &uri_explicit_port ); -- cgit v1.2.3-55-g7522 From ec746c00012317ba228d7043fa141d5e6a48c17c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 17 Jan 2022 16:11:56 +0000 Subject: [efi] Allow for autoexec scripts that are not located in a filesystem Signed-off-by: Michael Brown --- src/interface/efi/efi_autoexec.c | 62 ++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/src/interface/efi/efi_autoexec.c b/src/interface/efi/efi_autoexec.c index 88eb379bb..881c30c7e 100644 --- a/src/interface/efi/efi_autoexec.c +++ b/src/interface/efi/efi_autoexec.c @@ -39,10 +39,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ /** Autoexec script filename */ -#define AUTOEXEC_FILENAME L"autoexec.ipxe" +static wchar_t efi_autoexec_wname[] = L"autoexec.ipxe"; /** Autoexec script image name */ -#define AUTOEXEC_NAME "autoexec.ipxe" +static char efi_autoexec_name[] = "autoexec.ipxe"; /** Autoexec script (if any) */ static void *efi_autoexec; @@ -51,21 +51,21 @@ static void *efi_autoexec; static size_t efi_autoexec_len; /** - * Load autoexec script + * Load autoexec script from filesystem * * @v device Device handle * @ret rc Return status code */ -int efi_autoexec_load ( EFI_HANDLE device ) { +static int efi_autoexec_filesystem ( EFI_HANDLE device ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - static wchar_t name[] = AUTOEXEC_FILENAME; union { void *interface; EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs; } u; struct { EFI_FILE_INFO info; - CHAR16 name[ sizeof ( name ) / sizeof ( name[0] ) ]; + CHAR16 name[ sizeof ( efi_autoexec_wname ) / + sizeof ( efi_autoexec_wname[0] ) ]; } info; EFI_FILE_PROTOCOL *root; EFI_FILE_PROTOCOL *file; @@ -74,10 +74,6 @@ int efi_autoexec_load ( EFI_HANDLE device ) { EFI_STATUS efirc; int rc; - /* Sanity check */ - assert ( efi_autoexec == NULL ); - assert ( efi_autoexec_len == 0 ); - /* Open simple file system protocol */ if ( ( efirc = bs->OpenProtocol ( device, &efi_simple_file_system_protocol_guid, @@ -99,11 +95,12 @@ int efi_autoexec_load ( EFI_HANDLE device ) { } /* Open autoexec script */ - if ( ( efirc = root->Open ( root, &file, name, + if ( ( efirc = root->Open ( root, &file, efi_autoexec_wname, EFI_FILE_MODE_READ, 0 ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( device, "EFI %s has no %ls: %s\n", - efi_handle_name ( device ), name, strerror ( rc ) ); + efi_handle_name ( device ), efi_autoexec_wname, + strerror ( rc ) ); goto err_open; } @@ -113,7 +110,8 @@ int efi_autoexec_load ( EFI_HANDLE device ) { &info ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( device, "EFI %s could not get %ls info: %s\n", - efi_handle_name ( device ), name, strerror ( rc ) ); + efi_handle_name ( device ), efi_autoexec_wname, + strerror ( rc ) ); goto err_getinfo; } size = info.info.FileSize; @@ -122,7 +120,7 @@ int efi_autoexec_load ( EFI_HANDLE device ) { if ( ! size ) { rc = -EINVAL; DBGC ( device, "EFI %s has zero-length %ls\n", - efi_handle_name ( device ), name ); + efi_handle_name ( device ), efi_autoexec_wname ); goto err_empty; } @@ -131,7 +129,8 @@ int efi_autoexec_load ( EFI_HANDLE device ) { &data ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( device, "EFI %s could not allocate %ls: %s\n", - efi_handle_name ( device ), name, strerror ( rc ) ); + efi_handle_name ( device ), efi_autoexec_wname, + strerror ( rc ) ); goto err_alloc; } @@ -139,7 +138,8 @@ int efi_autoexec_load ( EFI_HANDLE device ) { if ( ( efirc = file->Read ( file, &size, data ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( device, "EFI %s could not read %ls: %s\n", - efi_handle_name ( device ), name, strerror ( rc ) ); + efi_handle_name ( device ), efi_autoexec_wname, + strerror ( rc ) ); goto err_read; } @@ -148,7 +148,7 @@ int efi_autoexec_load ( EFI_HANDLE device ) { efi_autoexec_len = size; data = NULL; DBGC ( device, "EFI %s found %ls\n", - efi_handle_name ( device ), name ); + efi_handle_name ( device ), efi_autoexec_wname ); /* Success */ rc = 0; @@ -169,6 +169,26 @@ int efi_autoexec_load ( EFI_HANDLE device ) { return rc; } +/** + * Load autoexec script + * + * @v device Device handle + * @ret rc Return status code + */ +int efi_autoexec_load ( EFI_HANDLE device ) { + int rc; + + /* Sanity check */ + assert ( efi_autoexec == NULL ); + assert ( efi_autoexec_len == 0 ); + + /* Try loading from file system, if supported */ + if ( ( rc = efi_autoexec_filesystem ( device ) ) == 0 ) + return 0; + + return -ENOENT; +} + /** * Register autoexec script * @@ -176,7 +196,6 @@ int efi_autoexec_load ( EFI_HANDLE device ) { static void efi_autoexec_startup ( void ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_HANDLE device = efi_loaded_image->DeviceHandle; - const char *name = AUTOEXEC_NAME; struct image *image; /* Do nothing if we have no autoexec script */ @@ -184,15 +203,16 @@ static void efi_autoexec_startup ( void ) { return; /* Create autoexec image */ - image = image_memory ( name, virt_to_user ( efi_autoexec ), + image = image_memory ( efi_autoexec_name, + virt_to_user ( efi_autoexec ), efi_autoexec_len ); if ( ! image ) { DBGC ( device, "EFI %s could not create %s\n", - efi_handle_name ( device ), name ); + efi_handle_name ( device ), efi_autoexec_name ); return; } DBGC ( device, "EFI %s registered %s\n", - efi_handle_name ( device ), name ); + efi_handle_name ( device ), efi_autoexec_name ); /* Free temporary copy */ bs->FreePool ( efi_autoexec ); -- cgit v1.2.3-55-g7522 From 6ba671acd922ee046b257c5119b8a0f64d275473 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 17 Jan 2022 16:17:17 +0000 Subject: [efi] Attempt to fetch autoexec script via TFTP Attempt to fetch the autoexec.ipxe script via TFTP using the PXE base code protocol installed on the loaded image's device handle, if present. This provides a generic alternative to the use of an embedded script for chainloaded binaries, which is particularly useful in a UEFI Secure Boot environment since it allows the script to be modified without the need to sign a new binary. As a side effect, this also provides a third method for breaking the PXE chainloading loop (as an alternative to requiring an embedded script or custom DHCP server configuration). Signed-off-by: Michael Brown --- src/interface/efi/efi_autoexec.c | 176 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) diff --git a/src/interface/efi/efi_autoexec.c b/src/interface/efi/efi_autoexec.c index 881c30c7e..79d4a4caf 100644 --- a/src/interface/efi/efi_autoexec.c +++ b/src/interface/efi/efi_autoexec.c @@ -24,11 +24,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include +#include #include #include #include +#include #include #include +#include #include #include @@ -169,6 +172,175 @@ static int efi_autoexec_filesystem ( EFI_HANDLE device ) { return rc; } +/** + * Load autoexec script from TFTP server + * + * @v device Device handle + * @ret rc Return status code + */ +static int efi_autoexec_tftp ( EFI_HANDLE device ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + void *interface; + EFI_PXE_BASE_CODE_PROTOCOL *pxe; + } u; + EFI_PXE_BASE_CODE_MODE *mode; + EFI_PXE_BASE_CODE_PACKET *packet; + union { + struct in_addr in; + EFI_IP_ADDRESS ip; + } server; + size_t filename_max; + char *filename; + char *sep; + UINT64 size; + VOID *data; + EFI_STATUS efirc; + int rc; + + /* Open PXE base code protocol */ + if ( ( efirc = bs->OpenProtocol ( device, + &efi_pxe_base_code_protocol_guid, + &u.interface, efi_image_handle, + device, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -EEFI ( efirc ); + DBGC ( device, "EFI %s has no PXE base code instance: %s\n", + efi_handle_name ( device ), strerror ( rc ) ); + goto err_pxe; + } + + /* Do not attempt to parse DHCPv6 packets */ + mode = u.pxe->Mode; + if ( mode->UsingIpv6 ) { + rc = -ENOTSUP; + DBGC ( device, "EFI %s has IPv6 PXE base code\n", + efi_handle_name ( device ) ); + goto err_ipv6; + } + + /* Identify relevant reply packet */ + if ( mode->PxeReplyReceived && + mode->PxeReply.Dhcpv4.BootpBootFile[0] ) { + /* Use boot filename if present in PXE reply */ + DBGC ( device, "EFI %s using PXE reply filename\n", + efi_handle_name ( device ) ); + packet = &mode->PxeReply; + } else if ( mode->DhcpAckReceived && + mode->DhcpAck.Dhcpv4.BootpBootFile[0] ) { + /* Otherwise, use boot filename if present in DHCPACK */ + DBGC ( device, "EFI %s using DHCPACK filename\n", + efi_handle_name ( device ) ); + packet = &mode->DhcpAck; + } else if ( mode->ProxyOfferReceived && + mode->ProxyOffer.Dhcpv4.BootpBootFile[0] ) { + /* Otherwise, use boot filename if present in ProxyDHCPOFFER */ + DBGC ( device, "EFI %s using ProxyDHCPOFFER filename\n", + efi_handle_name ( device ) ); + packet = &mode->ProxyOffer; + } else { + /* No boot filename available */ + rc = -ENOENT; + DBGC ( device, "EFI %s has no PXE boot filename\n", + efi_handle_name ( device ) ); + goto err_packet; + } + + /* Allocate filename */ + filename_max = ( sizeof ( packet->Dhcpv4.BootpBootFile ) + + ( sizeof ( efi_autoexec_name ) - 1 /* NUL */ ) + + 1 /* NUL */ ); + filename = zalloc ( filename_max ); + if ( ! filename ) { + rc = -ENOMEM; + goto err_filename; + } + + /* Extract next-server address and boot filename */ + memset ( &server, 0, sizeof ( server ) ); + memcpy ( &server.in, packet->Dhcpv4.BootpSiAddr, + sizeof ( server.in ) ); + memcpy ( filename, packet->Dhcpv4.BootpBootFile, + sizeof ( packet->Dhcpv4.BootpBootFile ) ); + + /* Update filename to autoexec script name */ + sep = strrchr ( filename, '/' ); + if ( ! sep ) + sep = strrchr ( filename, '\\' ); + if ( ! sep ) + sep = ( filename - 1 ); + strcpy ( ( sep + 1 ), efi_autoexec_name ); + + /* Get file size */ + if ( ( efirc = u.pxe->Mtftp ( u.pxe, + EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, + NULL, FALSE, &size, NULL, &server.ip, + ( ( UINT8 * ) filename ), NULL, + FALSE ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( device, "EFI %s could not get size of %s:%s: %s\n", + efi_handle_name ( device ), inet_ntoa ( server.in ), + filename, strerror ( rc ) ); + goto err_size; + } + + /* Ignore zero-length files */ + if ( ! size ) { + rc = -EINVAL; + DBGC ( device, "EFI %s has zero-length %s:%s\n", + efi_handle_name ( device ), inet_ntoa ( server.in ), + filename ); + goto err_empty; + } + + /* Allocate temporary copy */ + if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, size, + &data ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( device, "EFI %s could not allocate %s:%s: %s\n", + efi_handle_name ( device ), inet_ntoa ( server.in ), + filename, strerror ( rc ) ); + goto err_alloc; + } + + /* Download file */ + if ( ( efirc = u.pxe->Mtftp ( u.pxe, EFI_PXE_BASE_CODE_TFTP_READ_FILE, + data, FALSE, &size, NULL, &server.ip, + ( ( UINT8 * ) filename ), NULL, + FALSE ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( device, "EFI %s could not download %s:%s: %s\n", + efi_handle_name ( device ), inet_ntoa ( server.in ), + filename, strerror ( rc ) ); + goto err_download; + } + + /* Record autoexec script */ + efi_autoexec = data; + efi_autoexec_len = size; + data = NULL; + DBGC ( device, "EFI %s found %s:%s\n", efi_handle_name ( device ), + inet_ntoa ( server.in ), filename ); + + /* Success */ + rc = 0; + + err_download: + if ( data ) + bs->FreePool ( data ); + err_alloc: + err_empty: + err_size: + free ( filename ); + err_filename: + err_packet: + err_ipv6: + bs->CloseProtocol ( device, &efi_pxe_base_code_protocol_guid, + efi_image_handle, device ); + err_pxe: + return rc; +} + /** * Load autoexec script * @@ -186,6 +358,10 @@ int efi_autoexec_load ( EFI_HANDLE device ) { if ( ( rc = efi_autoexec_filesystem ( device ) ) == 0 ) return 0; + /* Try loading via TFTP, if supported */ + if ( ( rc = efi_autoexec_tftp ( device ) ) == 0 ) + return 0; + return -ENOENT; } -- cgit v1.2.3-55-g7522 From bc35b24e3ebd2996b2484b7f9ceb96a3cf25823a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 2 Feb 2022 03:26:20 +0000 Subject: [prefix] Fix use of writable code segment on 486 and earlier CPUs In real mode, code segments are always writable. In protected mode, code segments can never be writable. The precise implementation of this attribute differs between CPU generations, with subtly different behaviour arising on the transitions from protected mode to real mode. At the point of transition (when the PE bit is cleared in CR0) the hidden portion of the %cs descriptor will retain whatever attributes were in place for the protected-mode code segment, including the fact that the segment is not writable. The immediately following code will perform a far control flow transfer (such as ljmp or lret) in order to load a real-mode value into %cs. On the Pentium and later CPUs, the retained protected-mode attributes will be ignored for any accesses via %cs while the CPU is in real mode. A write via %cs will therefore be allowed even though the hidden portion of the %cs descriptor still describes a non-writable segment. On the 486 and earlier CPUs, the retained protected-mode attributes will not be ignored for accesses via %cs. A write via %cs will therefore cause a CPU fault. To obtain normal real-mode behaviour (i.e. a writable %cs descriptor), special logic is added to the ljmp instruction that populates the hidden portion of the %cs descriptor with real-mode attributes when a far jump is executed in real mode. The result is that writes via %cs will cause a CPU fault until the first ljmp instruction is executed, after which writes via %cs will be allowed as expected in real mode. The transition code in libprefix.S currently uses lret to load a real-mode value into %cs after clearing the PE bit. Experimentation shows that only the ljmp instruction will work to load real-mode attributes into the hidden portion of the %cs descriptor: other far control flow transfers (such as lret, lcall, or int) do not do so. When running on a 486 or earlier CPU, this results in code within libprefix.S running with a non-writable code segment after a mode transition, which in turn results in a CPU fault when real-mode code in liba20.S attempts to write to %cs:enable_a20_method. Fix by constructing and executing an ljmp instruction, to trigger the relevant descriptor population logic on 486 and earlier CPUs. This ljmp instruction is constructed on the stack, since the .prefix section may be executing directly from ROM (or from memory that the BIOS has write-protected in order to emulate an ISA ROM region) and so cannot be modified. Reported-by: Nikolai Zhubr Signed-off-by: Michael Brown --- src/arch/x86/prefix/libprefix.S | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/arch/x86/prefix/libprefix.S b/src/arch/x86/prefix/libprefix.S index ffb211058..d7f261957 100644 --- a/src/arch/x86/prefix/libprefix.S +++ b/src/arch/x86/prefix/libprefix.S @@ -380,6 +380,11 @@ process_bytes: pushl %eax pushl %ebp + /* Construct ljmp code on stack (since .prefix may not be writable) */ + .equ LJMP_LEN, 0x06 + pushw %cs /* "nop ; ljmp %cs, $2f" */ + pushw $2f + pushw $0xea90 /* Construct GDT on stack (since .prefix may not be writable) */ .equ GDT_LEN, 0x20 .equ PM_DS, 0x18 /* Flat data segment */ @@ -410,8 +415,9 @@ process_bytes: pushw %es pushw %ds pushw %ss - pushw %cs - pushw $2f + pushw %ss /* Far pointer to ljmp code on stack */ + leaw (GDT_LEN + 1)(%bp), %ax + pushw %ax cli data32 lgdt (%bp) movl %cr0, %eax @@ -438,7 +444,7 @@ process_bytes: popfw movl %eax, %cr0 lret -2: /* lret will ljmp to here */ +2: /* lret will ljmp to here (via constructed ljmp on stack) */ popw %ss popw %ds popw %es @@ -461,7 +467,7 @@ process_bytes: /* Restore GDT */ data32 lgdt -8(%bp) - leaw GDT_LEN(%bp), %sp + leaw (GDT_LEN + LJMP_LEN)(%bp), %sp /* Restore registers and return */ popl %ebp -- cgit v1.2.3-55-g7522 From 64113751c3729a2deaffbb1e1b6fd36e2c8bac6b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 10 Feb 2022 12:47:25 +0000 Subject: [efi] Enable IMAGE_GZIP by default for AArch64 AArch64 kernels tend to be distributed as gzip compressed images. Enable IMAGE_GZIP by default for AArch64 to avoid the need for uncompressed images to be provided. Originally-implemented-by: Alessandro Di Stefano Signed-off-by: Michael Brown --- src/config/defaults/efi.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/config/defaults/efi.h b/src/config/defaults/efi.h index 9ef34ab62..efa801819 100644 --- a/src/config/defaults/efi.h +++ b/src/config/defaults/efi.h @@ -58,4 +58,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define NAP_EFIARM #endif +#if defined ( __aarch64__ ) +#define IMAGE_GZIP /* GZIP image support */ +#endif + #endif /* CONFIG_DEFAULTS_EFI_H */ -- cgit v1.2.3-55-g7522 From f51a62bc3f7abb40e331c16df1f4d9314aefaf23 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Feb 2022 15:54:39 +0000 Subject: [console] Generalise bios_keymap() as key_remap() Allow the keyboard remapping functionality to be exposed to consoles other than the BIOS console. Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/bios_console.c | 18 +--------- src/core/keymap.c | 52 ++++++++++++++++++++++++++++ src/include/ipxe/keymap.h | 2 ++ 3 files changed, 55 insertions(+), 17 deletions(-) create mode 100644 src/core/keymap.c diff --git a/src/arch/x86/interface/pcbios/bios_console.c b/src/arch/x86/interface/pcbios/bios_console.c index 80ebf330e..0692e7a6c 100644 --- a/src/arch/x86/interface/pcbios/bios_console.c +++ b/src/arch/x86/interface/pcbios/bios_console.c @@ -339,22 +339,6 @@ static const char * bios_ansi_seq ( unsigned int scancode ) { return NULL; } -/** - * Map a key - * - * @v character Character read from console - * @ret character Mapped character - */ -static int bios_keymap ( unsigned int character ) { - struct key_mapping *mapping; - - for_each_table_entry ( mapping, KEYMAP ) { - if ( mapping->from == character ) - return mapping->to; - } - return character; -} - /** * Get character from BIOS console * @@ -387,7 +371,7 @@ static int bios_getchar ( void ) { /* If it's a normal character, just map and return it */ if ( character && ( character < 0x80 ) ) - return bios_keymap ( character ); + return key_remap ( character ); /* Otherwise, check for a special key that we know about */ if ( ( ansi_seq = bios_ansi_seq ( keypress >> 8 ) ) ) { diff --git a/src/core/keymap.c b/src/core/keymap.c new file mode 100644 index 000000000..a6707a2ce --- /dev/null +++ b/src/core/keymap.c @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2022 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** @file + * + * Keyboard mappings + * + */ + +/** + * Remap a key + * + * @v character Character read from console + * @ret character Mapped character + */ +unsigned int key_remap ( unsigned int character ) { + struct key_mapping *mapping; + + /* Remap via table */ + for_each_table_entry ( mapping, KEYMAP ) { + if ( mapping->from == character ) { + character = mapping->to; + break; + } + } + + return character; +} diff --git a/src/include/ipxe/keymap.h b/src/include/ipxe/keymap.h index 0f1b0c656..62b3bb131 100644 --- a/src/include/ipxe/keymap.h +++ b/src/include/ipxe/keymap.h @@ -27,4 +27,6 @@ struct key_mapping { /** Define a keyboard mapping */ #define __keymap __table_entry ( KEYMAP, 01 ) +extern unsigned int key_remap ( unsigned int character ); + #endif /* _IPXE_KEYMAP_H */ -- cgit v1.2.3-55-g7522 From fa708015e545a5877b1c07e2acf40f25b333c2cd Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Feb 2022 16:06:11 +0000 Subject: [console] Avoid attempting to remap numeric keypad on BIOS console To minimise code size, our keyboard mapping works on the basis of allowing the BIOS to convert the keyboard scancode into an ASCII character and then remapping the ASCII character. This causes problems with keyboard layouts such as "fr" that swap the shifted and unshifted digit keys, since the ASCII-based remapping will spuriously remap the numeric keypad (which produces the same ASCII values as the digit keys). Fix by checking that the keyboard scancode is within the range of keys that vary between keyboard mappings before attempting to remap the ASCII character. Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/bios_console.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/arch/x86/interface/pcbios/bios_console.c b/src/arch/x86/interface/pcbios/bios_console.c index 0692e7a6c..443513e9e 100644 --- a/src/arch/x86/interface/pcbios/bios_console.c +++ b/src/arch/x86/interface/pcbios/bios_console.c @@ -60,6 +60,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ATTR_DEFAULT ATTR_FCOL_WHITE +/** Maximum keycode subject to remapping + * + * This allows us to avoid remapping the numeric keypad, which is + * necessary for keyboard layouts such as "fr" that swap the shifted + * and unshifted digit keys. + */ +#define SCANCODE_RSHIFT 0x36 + /* Set default console usage if applicable */ #if ! ( defined ( CONSOLE_PCBIOS ) && CONSOLE_EXPLICIT ( CONSOLE_PCBIOS ) ) #undef CONSOLE_PCBIOS @@ -346,6 +354,7 @@ static const char * bios_ansi_seq ( unsigned int scancode ) { */ static int bios_getchar ( void ) { uint16_t keypress; + unsigned int scancode; unsigned int character; const char *ansi_seq; @@ -367,11 +376,17 @@ static int bios_getchar ( void ) { : "=a" ( keypress ) : "a" ( 0x1000 ), "m" ( bios_inject_lock ) ); bios_inject_lock--; + scancode = ( keypress >> 8 ); character = ( keypress & 0xff ); - /* If it's a normal character, just map and return it */ - if ( character && ( character < 0x80 ) ) - return key_remap ( character ); + /* If it's a normal character, map (if applicable) and return it */ + if ( character && ( character < 0x80 ) ) { + if ( scancode < SCANCODE_RSHIFT ) { + return key_remap ( character ); + } else { + return character; + } + } /* Otherwise, check for a special key that we know about */ if ( ( ansi_seq = bios_ansi_seq ( keypress >> 8 ) ) ) { -- cgit v1.2.3-55-g7522 From 468980db2b99f54ffec7d2a44c281308b4eec3c0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Feb 2022 21:54:47 +0000 Subject: [usb] Support keyboard remapping via the native USB keyboard driver Signed-off-by: Michael Brown --- src/drivers/usb/usbkbd.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/drivers/usb/usbkbd.c b/src/drivers/usb/usbkbd.c index a8ab6ab76..397ed77ec 100644 --- a/src/drivers/usb/usbkbd.c +++ b/src/drivers/usb/usbkbd.c @@ -29,6 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include "usbkbd.h" @@ -122,6 +123,10 @@ static unsigned int usbkbd_map ( unsigned int keycode, unsigned int modifiers, key = 0; } + /* Remap key if applicable */ + if ( keycode < USBKBD_KEY_CAPS_LOCK ) + key = key_remap ( key ); + return key; } -- cgit v1.2.3-55-g7522 From eb92ba0a4fe6b1b56e3ca5edfb93fdd545bc901a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 10 Feb 2022 11:55:54 +0000 Subject: [usb] Handle upper/lower case and Ctrl- after applying remapping Some keyboard layouts (e.g. "fr") swap letter and punctuation keys. Apply the logic for upper and lower case and for Ctrl- only after applying remapping, in order to handle these layouts correctly. Signed-off-by: Michael Brown --- src/drivers/usb/usbkbd.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/drivers/usb/usbkbd.c b/src/drivers/usb/usbkbd.c index 397ed77ec..ba4b2d4d7 100644 --- a/src/drivers/usb/usbkbd.c +++ b/src/drivers/usb/usbkbd.c @@ -25,6 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include #include #include #include @@ -70,12 +71,6 @@ static unsigned int usbkbd_map ( unsigned int keycode, unsigned int modifiers, } else if ( keycode <= USBKBD_KEY_Z ) { /* Alphabetic keys */ key = ( keycode - USBKBD_KEY_A + 'a' ); - if ( modifiers & USBKBD_CTRL ) { - key -= ( 'a' - CTRL_A ); - } else if ( ( modifiers & USBKBD_SHIFT ) || - ( leds & USBKBD_LED_CAPS_LOCK ) ) { - key -= ( 'a' - 'A' ); - } } else if ( keycode <= USBKBD_KEY_0 ) { /* Numeric key row */ if ( modifiers & USBKBD_SHIFT ) { @@ -127,6 +122,16 @@ static unsigned int usbkbd_map ( unsigned int keycode, unsigned int modifiers, if ( keycode < USBKBD_KEY_CAPS_LOCK ) key = key_remap ( key ); + /* Handle upper/lower case and Ctrl- */ + if ( islower ( key ) ) { + if ( modifiers & USBKBD_CTRL ) { + key -= ( 'a' - CTRL_A ); + } else if ( ( modifiers & USBKBD_SHIFT ) || + ( leds & USBKBD_LED_CAPS_LOCK ) ) { + key -= ( 'a' - 'A' ); + } + } + return key; } -- cgit v1.2.3-55-g7522 From 0979b3a11ddd642b047c7e9240cefc0144c595c7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 10 Feb 2022 12:14:46 +0000 Subject: [efi] Support keyboard remapping via the EFI console Signed-off-by: Michael Brown --- src/interface/efi/efi_console.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/interface/efi/efi_console.c b/src/interface/efi/efi_console.c index 98ebbf3ac..874f54b6c 100644 --- a/src/interface/efi/efi_console.c +++ b/src/interface/efi/efi_console.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include @@ -317,6 +318,13 @@ static int efi_getchar ( void ) { key.KeyState.KeyToggleState, key.Key.UnicodeChar, key.Key.ScanCode ); + /* Remap key. There is unfortunately no way to avoid + * remapping the numeric keypad, since EFI destroys the scan + * code information that would allow us to differentiate + * between main keyboard and numeric keypad. + */ + key.Key.UnicodeChar = key_remap ( key.Key.UnicodeChar ); + /* Translate Ctrl- */ if ( ( key.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID ) && ( key.KeyState.KeyShiftState & ( EFI_LEFT_CONTROL_PRESSED | -- cgit v1.2.3-55-g7522 From 3f05a82fec6223a49df300a9cbf80c6245c3f99e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Sun, 6 Feb 2022 19:33:20 +0000 Subject: [console] Update genkeymap to work with current databases Rewrite genkeymap.pl in Python with added sanity checks, and update the list of keyboard mappings to remove those no longer supported by the underlying "loadkeys" tool. Signed-off-by: Michael Brown --- src/Makefile | 2 +- src/hci/keymap/keymap_al.c | 1 + src/hci/keymap/keymap_az.c | 2 +- src/hci/keymap/keymap_bg.c | 15 -- src/hci/keymap/keymap_cz.c | 33 +++- src/hci/keymap/keymap_de.c | 13 +- src/hci/keymap/keymap_dk.c | 4 +- src/hci/keymap/keymap_es.c | 2 + src/hci/keymap/keymap_et.c | 1 - src/hci/keymap/keymap_fi.c | 9 - src/hci/keymap/keymap_fr.c | 16 +- src/hci/keymap/keymap_hu.c | 1 + src/hci/keymap/keymap_il.c | 12 ++ src/hci/keymap/keymap_it.c | 1 + src/hci/keymap/keymap_mt.c | 2 +- src/hci/keymap/keymap_nl.c | 4 +- src/hci/keymap/keymap_no-latin1.c | 1 + src/hci/keymap/keymap_no.c | 88 +--------- src/hci/keymap/keymap_pt.c | 31 ++-- src/hci/keymap/keymap_ru.c | 1 + src/hci/keymap/keymap_sr-latin.c | 15 ++ src/hci/keymap/keymap_sr.c | 35 ---- src/hci/keymap/keymap_th.c | 15 -- src/hci/keymap/keymap_wo.c | 55 ------ src/util/genkeymap.pl | 238 -------------------------- src/util/genkeymap.py | 346 ++++++++++++++++++++++++++++++++++++++ 26 files changed, 446 insertions(+), 497 deletions(-) delete mode 100644 src/hci/keymap/keymap_bg.c create mode 100644 src/hci/keymap/keymap_sr-latin.c delete mode 100644 src/hci/keymap/keymap_sr.c delete mode 100644 src/hci/keymap/keymap_th.c delete mode 100644 src/hci/keymap/keymap_wo.c delete mode 100755 src/util/genkeymap.pl create mode 100755 src/util/genkeymap.py diff --git a/src/Makefile b/src/Makefile index 4c4abf1aa..83642e789 100644 --- a/src/Makefile +++ b/src/Makefile @@ -50,7 +50,7 @@ ELF2EFI64 := ./util/elf2efi64 EFIROM := ./util/efirom EFIFATBIN := ./util/efifatbin EINFO := ./util/einfo -GENKEYMAP := ./util/genkeymap.pl +GENKEYMAP := ./util/genkeymap.py DOXYGEN := doxygen LCAB := lcab QEMUIMG := qemu-img diff --git a/src/hci/keymap/keymap_al.c b/src/hci/keymap/keymap_al.c index caf295e8e..e4418361b 100644 --- a/src/hci/keymap/keymap_al.c +++ b/src/hci/keymap/keymap_al.c @@ -14,6 +14,7 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); struct key_mapping al_mapping[] __keymap = { { 0x19, 0x1a }, /* Ctrl-Y => Ctrl-Z */ { 0x1a, 0x19 }, /* Ctrl-Z => Ctrl-Y */ + { 0x1c, 0x1d }, /* 0x1c => 0x1d */ { 0x22, 0x7b }, /* '"' => '{' */ { 0x27, 0x5b }, /* '\'' => '[' */ { 0x3c, 0x3b }, /* '<' => ';' */ diff --git a/src/hci/keymap/keymap_az.c b/src/hci/keymap/keymap_az.c index 27ce91e7d..525ab2336 100644 --- a/src/hci/keymap/keymap_az.c +++ b/src/hci/keymap/keymap_az.c @@ -12,7 +12,7 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "az" keyboard mapping */ struct key_mapping az_mapping[] __keymap = { - { 0x23, 0x27 }, /* '#' => '\'' */ + { 0x1e, 0x36 }, /* 0x1e => '6' */ { 0x24, 0x3b }, /* '$' => ';' */ { 0x26, 0x3f }, /* '&' => '?' */ { 0x2f, 0x2e }, /* '/' => '.' */ diff --git a/src/hci/keymap/keymap_bg.c b/src/hci/keymap/keymap_bg.c deleted file mode 100644 index 62b6baeac..000000000 --- a/src/hci/keymap/keymap_bg.c +++ /dev/null @@ -1,15 +0,0 @@ -/** @file - * - * "bg" keyboard mapping - * - * This file is automatically generated; do not edit - * - */ - -FILE_LICENCE ( PUBLIC_DOMAIN ); - -#include - -/** "bg" keyboard mapping */ -struct key_mapping bg_mapping[] __keymap = { -}; diff --git a/src/hci/keymap/keymap_cz.c b/src/hci/keymap/keymap_cz.c index 9280f84fd..2b4a21592 100644 --- a/src/hci/keymap/keymap_cz.c +++ b/src/hci/keymap/keymap_cz.c @@ -12,16 +12,35 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "cz" keyboard mapping */ struct key_mapping cz_mapping[] __keymap = { - { 0x21, 0x2b }, /* '!' => '+' */ + { 0x19, 0x1a }, /* Ctrl-Y => Ctrl-Z */ + { 0x1a, 0x19 }, /* Ctrl-Z => Ctrl-Y */ + { 0x1f, 0x1c }, /* 0x1f => 0x1c */ + { 0x21, 0x31 }, /* '!' => '1' */ + { 0x22, 0x21 }, /* '"' => '!' */ + { 0x23, 0x33 }, /* '#' => '3' */ + { 0x24, 0x34 }, /* '$' => '4' */ + { 0x25, 0x35 }, /* '%' => '5' */ + { 0x26, 0x37 }, /* '&' => '7' */ + { 0x28, 0x39 }, /* '(' => '9' */ + { 0x29, 0x30 }, /* ')' => '0' */ + { 0x2a, 0x38 }, /* '*' => '8' */ { 0x2d, 0x3d }, /* '-' => '=' */ { 0x2f, 0x2d }, /* '/' => '-' */ { 0x31, 0x2b }, /* '1' => '+' */ - { 0x3c, 0x2c }, /* '<' => ',' */ - { 0x3e, 0x2e }, /* '>' => '.' */ - { 0x3f, 0x2d }, /* '?' => '-' */ + { 0x3a, 0x22 }, /* ':' => '"' */ + { 0x3c, 0x3f }, /* '<' => '?' */ + { 0x3e, 0x3a }, /* '>' => ':' */ + { 0x3f, 0x5f }, /* '?' => '_' */ + { 0x40, 0x32 }, /* '@' => '2' */ + { 0x59, 0x5a }, /* 'Y' => 'Z' */ + { 0x5a, 0x59 }, /* 'Z' => 'Y' */ { 0x5d, 0x29 }, /* ']' => ')' */ - { 0x5f, 0x3d }, /* '_' => '=' */ + { 0x5e, 0x36 }, /* '^' => '6' */ + { 0x5f, 0x25 }, /* '_' => '%' */ { 0x60, 0x3b }, /* '`' => ';' */ - { 0x7d, 0x29 }, /* '}' => ')' */ - { 0x7e, 0x3b }, /* '~' => ';' */ + { 0x79, 0x7a }, /* 'y' => 'z' */ + { 0x7a, 0x79 }, /* 'z' => 'y' */ + { 0x7b, 0x2f }, /* '{' => '/' */ + { 0x7c, 0x27 }, /* '|' => '\'' */ + { 0x7d, 0x28 }, /* '}' => '(' */ }; diff --git a/src/hci/keymap/keymap_de.c b/src/hci/keymap/keymap_de.c index ffcf912f1..2559e1538 100644 --- a/src/hci/keymap/keymap_de.c +++ b/src/hci/keymap/keymap_de.c @@ -14,33 +14,26 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); struct key_mapping de_mapping[] __keymap = { { 0x19, 0x1a }, /* Ctrl-Y => Ctrl-Z */ { 0x1a, 0x19 }, /* Ctrl-Z => Ctrl-Y */ - { 0x22, 0x7d }, /* '"' => '}' */ + { 0x1c, 0x23 }, /* 0x1c => '#' */ + { 0x1d, 0x1e }, /* 0x1d => 0x1e */ + { 0x1e, 0x36 }, /* 0x1e => '6' */ { 0x26, 0x2f }, /* '&' => '/' */ - { 0x27, 0x5d }, /* '\'' => ']' */ { 0x28, 0x29 }, /* '(' => ')' */ { 0x29, 0x3d }, /* ')' => '=' */ { 0x2a, 0x28 }, /* '*' => '(' */ - { 0x2b, 0x60 }, /* '+' => '`' */ - { 0x2d, 0x5c }, /* '-' => '\\' */ { 0x2f, 0x2d }, /* '/' => '-' */ - { 0x3a, 0x7b }, /* ':' => '{' */ - { 0x3b, 0x5b }, /* ';' => '[' */ { 0x3c, 0x3b }, /* '<' => ';' */ - { 0x3d, 0x27 }, /* '=' => '\'' */ { 0x3e, 0x3a }, /* '>' => ':' */ { 0x3f, 0x5f }, /* '?' => '_' */ { 0x40, 0x22 }, /* '@' => '"' */ { 0x59, 0x5a }, /* 'Y' => 'Z' */ { 0x5a, 0x59 }, /* 'Z' => 'Y' */ - { 0x5b, 0x40 }, /* '[' => '@' */ { 0x5c, 0x23 }, /* '\\' => '#' */ { 0x5d, 0x2b }, /* ']' => '+' */ { 0x5e, 0x26 }, /* '^' => '&' */ { 0x5f, 0x3f }, /* '_' => '?' */ - { 0x60, 0x5e }, /* '`' => '^' */ { 0x79, 0x7a }, /* 'y' => 'z' */ { 0x7a, 0x79 }, /* 'z' => 'y' */ - { 0x7b, 0x5c }, /* '{' => '\\' */ { 0x7c, 0x27 }, /* '|' => '\'' */ { 0x7d, 0x2a }, /* '}' => '*' */ }; diff --git a/src/hci/keymap/keymap_dk.c b/src/hci/keymap/keymap_dk.c index e409018c8..05110dc89 100644 --- a/src/hci/keymap/keymap_dk.c +++ b/src/hci/keymap/keymap_dk.c @@ -12,11 +12,12 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "dk" keyboard mapping */ struct key_mapping dk_mapping[] __keymap = { + { 0x1c, 0x27 }, /* 0x1c => '\'' */ + { 0x1e, 0x36 }, /* 0x1e => '6' */ { 0x26, 0x2f }, /* '&' => '/' */ { 0x28, 0x29 }, /* '(' => ')' */ { 0x29, 0x3d }, /* ')' => '=' */ { 0x2a, 0x28 }, /* '*' => '(' */ - { 0x2b, 0x60 }, /* '+' => '`' */ { 0x2d, 0x2b }, /* '-' => '+' */ { 0x2f, 0x2d }, /* '/' => '-' */ { 0x3c, 0x3b }, /* '<' => ';' */ @@ -27,5 +28,4 @@ struct key_mapping dk_mapping[] __keymap = { { 0x5e, 0x26 }, /* '^' => '&' */ { 0x5f, 0x3f }, /* '_' => '?' */ { 0x7c, 0x2a }, /* '|' => '*' */ - { 0x7d, 0x5e }, /* '}' => '^' */ }; diff --git a/src/hci/keymap/keymap_es.c b/src/hci/keymap/keymap_es.c index c1fe013a9..51dedfff7 100644 --- a/src/hci/keymap/keymap_es.c +++ b/src/hci/keymap/keymap_es.c @@ -12,6 +12,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "es" keyboard mapping */ struct key_mapping es_mapping[] __keymap = { + { 0x1c, 0x1d }, /* 0x1c => 0x1d */ + { 0x1e, 0x36 }, /* 0x1e => '6' */ { 0x26, 0x2f }, /* '&' => '/' */ { 0x28, 0x29 }, /* '(' => ')' */ { 0x29, 0x3d }, /* ')' => '=' */ diff --git a/src/hci/keymap/keymap_et.c b/src/hci/keymap/keymap_et.c index ad88cecc1..dd0f879b1 100644 --- a/src/hci/keymap/keymap_et.c +++ b/src/hci/keymap/keymap_et.c @@ -26,5 +26,4 @@ struct key_mapping et_mapping[] __keymap = { { 0x5e, 0x26 }, /* '^' => '&' */ { 0x5f, 0x3f }, /* '_' => '?' */ { 0x7c, 0x2a }, /* '|' => '*' */ - { 0x7f, 0x1b }, /* 0x7f => 0x1b */ }; diff --git a/src/hci/keymap/keymap_fi.c b/src/hci/keymap/keymap_fi.c index c8f6c3a06..c489bf0e5 100644 --- a/src/hci/keymap/keymap_fi.c +++ b/src/hci/keymap/keymap_fi.c @@ -12,27 +12,18 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "fi" keyboard mapping */ struct key_mapping fi_mapping[] __keymap = { - { 0x22, 0x5b }, /* '"' => '[' */ { 0x26, 0x2f }, /* '&' => '/' */ - { 0x27, 0x7b }, /* '\'' => '{' */ { 0x28, 0x29 }, /* '(' => ')' */ { 0x29, 0x3d }, /* ')' => '=' */ { 0x2a, 0x28 }, /* '*' => '(' */ - { 0x2b, 0x60 }, /* '+' => '`' */ { 0x2d, 0x2b }, /* '-' => '+' */ { 0x2f, 0x2d }, /* '/' => '-' */ - { 0x3a, 0x5c }, /* ':' => '\\' */ - { 0x3b, 0x7c }, /* ';' => '|' */ { 0x3c, 0x3b }, /* '<' => ';' */ - { 0x3d, 0x27 }, /* '=' => '\'' */ { 0x3e, 0x3a }, /* '>' => ':' */ { 0x3f, 0x5f }, /* '?' => '_' */ { 0x40, 0x22 }, /* '@' => '"' */ - { 0x5b, 0x7d }, /* '[' => '}' */ { 0x5c, 0x27 }, /* '\\' => '\'' */ { 0x5e, 0x26 }, /* '^' => '&' */ { 0x5f, 0x3f }, /* '_' => '?' */ - { 0x7b, 0x5d }, /* '{' => ']' */ { 0x7c, 0x2a }, /* '|' => '*' */ - { 0x7d, 0x5e }, /* '}' => '^' */ }; diff --git a/src/hci/keymap/keymap_fr.c b/src/hci/keymap/keymap_fr.c index fd615a456..8f3b4999d 100644 --- a/src/hci/keymap/keymap_fr.c +++ b/src/hci/keymap/keymap_fr.c @@ -16,13 +16,16 @@ struct key_mapping fr_mapping[] __keymap = { { 0x11, 0x01 }, /* Ctrl-Q => Ctrl-A */ { 0x17, 0x1a }, /* Ctrl-W => Ctrl-Z */ { 0x1a, 0x17 }, /* Ctrl-Z => Ctrl-W */ + { 0x1c, 0x2a }, /* 0x1c => '*' */ + { 0x1d, 0x24 }, /* 0x1d => '$' */ + { 0x1e, 0x1c }, /* 0x1e => 0x1c */ + { 0x1f, 0x1d }, /* 0x1f => 0x1d */ { 0x21, 0x31 }, /* '!' => '1' */ { 0x22, 0x25 }, /* '"' => '%' */ { 0x23, 0x33 }, /* '#' => '3' */ { 0x24, 0x34 }, /* '$' => '4' */ { 0x25, 0x35 }, /* '%' => '5' */ { 0x26, 0x37 }, /* '&' => '7' */ - { 0x27, 0x7c }, /* '\'' => '|' */ { 0x28, 0x39 }, /* '(' => '9' */ { 0x29, 0x30 }, /* ')' => '0' */ { 0x2a, 0x38 }, /* '*' => '8' */ @@ -30,39 +33,28 @@ struct key_mapping fr_mapping[] __keymap = { { 0x2d, 0x29 }, /* '-' => ')' */ { 0x2e, 0x3a }, /* '.' => ':' */ { 0x2f, 0x21 }, /* '/' => '!' */ - { 0x30, 0x40 }, /* '0' => '@' */ { 0x31, 0x26 }, /* '1' => '&' */ - { 0x32, 0x7b }, /* '2' => '{' */ { 0x33, 0x22 }, /* '3' => '"' */ { 0x34, 0x27 }, /* '4' => '\'' */ { 0x35, 0x28 }, /* '5' => '(' */ { 0x36, 0x2d }, /* '6' => '-' */ - { 0x37, 0x7d }, /* '7' => '}' */ { 0x38, 0x5f }, /* '8' => '_' */ - { 0x39, 0x2f }, /* '9' => '/' */ { 0x3a, 0x4d }, /* ':' => 'M' */ { 0x3b, 0x6d }, /* ';' => 'm' */ { 0x3c, 0x2e }, /* '<' => '.' */ { 0x3e, 0x2f }, /* '>' => '/' */ - { 0x3f, 0x5c }, /* '?' => '\\' */ { 0x40, 0x32 }, /* '@' => '2' */ { 0x41, 0x51 }, /* 'A' => 'Q' */ { 0x4d, 0x3f }, /* 'M' => '?' */ { 0x51, 0x41 }, /* 'Q' => 'A' */ { 0x57, 0x5a }, /* 'W' => 'Z' */ { 0x5a, 0x57 }, /* 'Z' => 'W' */ - { 0x5b, 0x5e }, /* '[' => '^' */ { 0x5c, 0x2a }, /* '\\' => '*' */ { 0x5d, 0x24 }, /* ']' => '$' */ { 0x5e, 0x36 }, /* '^' => '6' */ - { 0x5f, 0x5d }, /* '_' => ']' */ - { 0x60, 0x2a }, /* '`' => '*' */ { 0x61, 0x71 }, /* 'a' => 'q' */ { 0x6d, 0x2c }, /* 'm' => ',' */ { 0x71, 0x61 }, /* 'q' => 'a' */ { 0x77, 0x7a }, /* 'w' => 'z' */ { 0x7a, 0x77 }, /* 'z' => 'w' */ - { 0x7b, 0x3c }, /* '{' => '<' */ - { 0x7c, 0x23 }, /* '|' => '#' */ - { 0x7d, 0x3e }, /* '}' => '>' */ }; diff --git a/src/hci/keymap/keymap_hu.c b/src/hci/keymap/keymap_hu.c index 68eff2f4c..a2eadbc62 100644 --- a/src/hci/keymap/keymap_hu.c +++ b/src/hci/keymap/keymap_hu.c @@ -14,6 +14,7 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); struct key_mapping hu_mapping[] __keymap = { { 0x19, 0x1a }, /* Ctrl-Y => Ctrl-Z */ { 0x1a, 0x19 }, /* Ctrl-Z => Ctrl-Y */ + { 0x1e, 0x36 }, /* 0x1e => '6' */ { 0x21, 0x27 }, /* '!' => '\'' */ { 0x23, 0x2b }, /* '#' => '+' */ { 0x24, 0x21 }, /* '$' => '!' */ diff --git a/src/hci/keymap/keymap_il.c b/src/hci/keymap/keymap_il.c index 478330c0c..f631f7ac9 100644 --- a/src/hci/keymap/keymap_il.c +++ b/src/hci/keymap/keymap_il.c @@ -12,4 +12,16 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "il" keyboard mapping */ struct key_mapping il_mapping[] __keymap = { + { 0x1d, 0x1b }, /* 0x1d => 0x1b */ + { 0x27, 0x2c }, /* '\'' => ',' */ + { 0x28, 0x29 }, /* '(' => ')' */ + { 0x29, 0x28 }, /* ')' => '(' */ + { 0x2f, 0x2e }, /* '/' => '.' */ + { 0x3c, 0x3e }, /* '<' => '>' */ + { 0x3e, 0x3c }, /* '>' => '<' */ + { 0x5b, 0x5d }, /* '[' => ']' */ + { 0x5d, 0x5b }, /* ']' => '[' */ + { 0x60, 0x3b }, /* '`' => ';' */ + { 0x7b, 0x7d }, /* '{' => '}' */ + { 0x7d, 0x7b }, /* '}' => '{' */ }; diff --git a/src/hci/keymap/keymap_it.c b/src/hci/keymap/keymap_it.c index 5bb05471c..d96102c9e 100644 --- a/src/hci/keymap/keymap_it.c +++ b/src/hci/keymap/keymap_it.c @@ -12,6 +12,7 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "it" keyboard mapping */ struct key_mapping it_mapping[] __keymap = { + { 0x1e, 0x36 }, /* 0x1e => '6' */ { 0x26, 0x2f }, /* '&' => '/' */ { 0x28, 0x29 }, /* '(' => ')' */ { 0x29, 0x3d }, /* ')' => '=' */ diff --git a/src/hci/keymap/keymap_mt.c b/src/hci/keymap/keymap_mt.c index 094a6fc60..dfca2ff66 100644 --- a/src/hci/keymap/keymap_mt.c +++ b/src/hci/keymap/keymap_mt.c @@ -12,8 +12,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "mt" keyboard mapping */ struct key_mapping mt_mapping[] __keymap = { + { 0x1c, 0x1e }, /* 0x1c => 0x1e */ { 0x22, 0x40 }, /* '"' => '@' */ - { 0x23, 0x04 }, /* '#' => Ctrl-D */ { 0x40, 0x22 }, /* '@' => '"' */ { 0x5c, 0x23 }, /* '\\' => '#' */ { 0x7c, 0x7e }, /* '|' => '~' */ diff --git a/src/hci/keymap/keymap_nl.c b/src/hci/keymap/keymap_nl.c index ba051705e..2a0fbbcbd 100644 --- a/src/hci/keymap/keymap_nl.c +++ b/src/hci/keymap/keymap_nl.c @@ -12,11 +12,13 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "nl" keyboard mapping */ struct key_mapping nl_mapping[] __keymap = { + { 0x1c, 0x3c }, /* 0x1c => '<' */ + { 0x1d, 0x1c }, /* 0x1d => 0x1c */ + { 0x1e, 0x36 }, /* 0x1e => '6' */ { 0x26, 0x5f }, /* '&' => '_' */ { 0x28, 0x29 }, /* '(' => ')' */ { 0x29, 0x27 }, /* ')' => '\'' */ { 0x2a, 0x28 }, /* '*' => '(' */ - { 0x2b, 0x7e }, /* '+' => '~' */ { 0x2d, 0x2f }, /* '-' => '/' */ { 0x2f, 0x2d }, /* '/' => '-' */ { 0x3b, 0x2b }, /* ';' => '+' */ diff --git a/src/hci/keymap/keymap_no-latin1.c b/src/hci/keymap/keymap_no-latin1.c index 8c3e81b31..655e4cef7 100644 --- a/src/hci/keymap/keymap_no-latin1.c +++ b/src/hci/keymap/keymap_no-latin1.c @@ -12,6 +12,7 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "no-latin1" keyboard mapping */ struct key_mapping no_latin1_mapping[] __keymap = { + { 0x1d, 0x1e }, /* 0x1d => 0x1e */ { 0x26, 0x2f }, /* '&' => '/' */ { 0x28, 0x29 }, /* '(' => ')' */ { 0x29, 0x3d }, /* ')' => '=' */ diff --git a/src/hci/keymap/keymap_no.c b/src/hci/keymap/keymap_no.c index 45cf9e847..7a2df7c5a 100644 --- a/src/hci/keymap/keymap_no.c +++ b/src/hci/keymap/keymap_no.c @@ -12,94 +12,22 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "no" keyboard mapping */ struct key_mapping no_mapping[] __keymap = { - { 0x02, 0x18 }, /* Ctrl-B => Ctrl-X */ - { 0x03, 0x0a }, /* Ctrl-C => Ctrl-J */ - { 0x04, 0x05 }, /* Ctrl-D => Ctrl-E */ - { 0x06, 0x15 }, /* Ctrl-F => Ctrl-U */ - { 0x07, 0x09 }, /* Ctrl-G => Ctrl-I */ - { 0x08, 0x04 }, /* Ctrl-H => Ctrl-D */ - { 0x0a, 0x08 }, /* Ctrl-J => Ctrl-H */ - { 0x0b, 0x14 }, /* Ctrl-K => Ctrl-T */ - { 0x0c, 0x0e }, /* Ctrl-L => Ctrl-N */ - { 0x0e, 0x02 }, /* Ctrl-N => Ctrl-B */ - { 0x0f, 0x12 }, /* Ctrl-O => Ctrl-R */ - { 0x10, 0x0c }, /* Ctrl-P => Ctrl-L */ - { 0x12, 0x10 }, /* Ctrl-R => Ctrl-P */ - { 0x13, 0x0f }, /* Ctrl-S => Ctrl-O */ - { 0x14, 0x19 }, /* Ctrl-T => Ctrl-Y */ - { 0x15, 0x07 }, /* Ctrl-U => Ctrl-G */ - { 0x16, 0x0b }, /* Ctrl-V => Ctrl-K */ - { 0x18, 0x11 }, /* Ctrl-X => Ctrl-Q */ - { 0x19, 0x06 }, /* Ctrl-Y => Ctrl-F */ - { 0x22, 0x5f }, /* '"' => '_' */ + { 0x1c, 0x27 }, /* 0x1c => '\'' */ + { 0x1e, 0x36 }, /* 0x1e => '6' */ { 0x26, 0x2f }, /* '&' => '/' */ - { 0x27, 0x2d }, /* '\'' => '-' */ { 0x28, 0x29 }, /* '(' => ')' */ { 0x29, 0x3d }, /* ')' => '=' */ { 0x2a, 0x28 }, /* '*' => '(' */ - { 0x2b, 0x60 }, /* '+' => '`' */ - { 0x2c, 0x77 }, /* ',' => 'w' */ { 0x2d, 0x2b }, /* '-' => '+' */ - { 0x2e, 0x76 }, /* '.' => 'v' */ - { 0x2f, 0x7a }, /* '/' => 'z' */ - { 0x3a, 0x53 }, /* ':' => 'S' */ - { 0x3b, 0x73 }, /* ';' => 's' */ - { 0x3c, 0x57 }, /* '<' => 'W' */ + { 0x2f, 0x2d }, /* '/' => '-' */ + { 0x3c, 0x3b }, /* '<' => ';' */ { 0x3d, 0x5c }, /* '=' => '\\' */ - { 0x3e, 0x56 }, /* '>' => 'V' */ - { 0x3f, 0x5a }, /* '?' => 'Z' */ + { 0x3e, 0x3a }, /* '>' => ':' */ + { 0x3f, 0x5f }, /* '?' => '_' */ { 0x40, 0x22 }, /* '@' => '"' */ - { 0x42, 0x58 }, /* 'B' => 'X' */ - { 0x43, 0x4a }, /* 'C' => 'J' */ - { 0x44, 0x45 }, /* 'D' => 'E' */ - { 0x45, 0x3a }, /* 'E' => ':' */ - { 0x46, 0x55 }, /* 'F' => 'U' */ - { 0x47, 0x49 }, /* 'G' => 'I' */ - { 0x48, 0x44 }, /* 'H' => 'D' */ - { 0x49, 0x43 }, /* 'I' => 'C' */ - { 0x4a, 0x48 }, /* 'J' => 'H' */ - { 0x4b, 0x54 }, /* 'K' => 'T' */ - { 0x4c, 0x4e }, /* 'L' => 'N' */ - { 0x4e, 0x42 }, /* 'N' => 'B' */ - { 0x4f, 0x52 }, /* 'O' => 'R' */ - { 0x50, 0x4c }, /* 'P' => 'L' */ - { 0x52, 0x50 }, /* 'R' => 'P' */ - { 0x53, 0x4f }, /* 'S' => 'O' */ - { 0x54, 0x59 }, /* 'T' => 'Y' */ - { 0x55, 0x47 }, /* 'U' => 'G' */ - { 0x56, 0x4b }, /* 'V' => 'K' */ - { 0x57, 0x3b }, /* 'W' => ';' */ - { 0x58, 0x51 }, /* 'X' => 'Q' */ - { 0x59, 0x46 }, /* 'Y' => 'F' */ - { 0x5b, 0x27 }, /* '[' => '\'' */ - { 0x5c, 0x3c }, /* '\\' => '<' */ - { 0x5d, 0x7e }, /* ']' => '~' */ + { 0x5c, 0x27 }, /* '\\' => '\'' */ { 0x5e, 0x26 }, /* '^' => '&' */ { 0x5f, 0x3f }, /* '_' => '?' */ { 0x60, 0x7c }, /* '`' => '|' */ - { 0x62, 0x78 }, /* 'b' => 'x' */ - { 0x63, 0x6a }, /* 'c' => 'j' */ - { 0x64, 0x65 }, /* 'd' => 'e' */ - { 0x65, 0x2e }, /* 'e' => '.' */ - { 0x66, 0x75 }, /* 'f' => 'u' */ - { 0x67, 0x69 }, /* 'g' => 'i' */ - { 0x68, 0x64 }, /* 'h' => 'd' */ - { 0x69, 0x63 }, /* 'i' => 'c' */ - { 0x6a, 0x68 }, /* 'j' => 'h' */ - { 0x6b, 0x74 }, /* 'k' => 't' */ - { 0x6c, 0x6e }, /* 'l' => 'n' */ - { 0x6e, 0x62 }, /* 'n' => 'b' */ - { 0x6f, 0x72 }, /* 'o' => 'r' */ - { 0x70, 0x6c }, /* 'p' => 'l' */ - { 0x72, 0x70 }, /* 'r' => 'p' */ - { 0x73, 0x6f }, /* 's' => 'o' */ - { 0x74, 0x79 }, /* 't' => 'y' */ - { 0x75, 0x67 }, /* 'u' => 'g' */ - { 0x76, 0x6b }, /* 'v' => 'k' */ - { 0x77, 0x2c }, /* 'w' => ',' */ - { 0x78, 0x71 }, /* 'x' => 'q' */ - { 0x79, 0x66 }, /* 'y' => 'f' */ - { 0x7b, 0x2a }, /* '{' => '*' */ - { 0x7c, 0x3e }, /* '|' => '>' */ - { 0x7d, 0x5e }, /* '}' => '^' */ + { 0x7c, 0x2a }, /* '|' => '*' */ }; diff --git a/src/hci/keymap/keymap_pt.c b/src/hci/keymap/keymap_pt.c index a8e44b6a3..b993902af 100644 --- a/src/hci/keymap/keymap_pt.c +++ b/src/hci/keymap/keymap_pt.c @@ -12,18 +12,21 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "pt" keyboard mapping */ struct key_mapping pt_mapping[] __keymap = { - { 0x1c, 0x1d }, /* 0x1c => 0x1d */ - { 0x1d, 0x1b }, /* 0x1d => 0x1b */ - { 0x22, 0x5e }, /* '"' => '^' */ - { 0x27, 0x7e }, /* '\'' => '~' */ - { 0x2f, 0x3b }, /* '/' => ';' */ - { 0x3f, 0x3a }, /* '?' => ':' */ - { 0x5b, 0x27 }, /* '[' => '\'' */ - { 0x5c, 0x5d }, /* '\\' => ']' */ - { 0x5d, 0x5b }, /* ']' => '[' */ - { 0x60, 0x27 }, /* '`' => '\'' */ - { 0x7b, 0x60 }, /* '{' => '`' */ - { 0x7c, 0x7d }, /* '|' => '}' */ - { 0x7d, 0x7b }, /* '}' => '{' */ - { 0x7e, 0x22 }, /* '~' => '"' */ + { 0x1e, 0x36 }, /* 0x1e => '6' */ + { 0x26, 0x2f }, /* '&' => '/' */ + { 0x28, 0x29 }, /* '(' => ')' */ + { 0x29, 0x3d }, /* ')' => '=' */ + { 0x2a, 0x28 }, /* '*' => '(' */ + { 0x2d, 0x27 }, /* '-' => '\'' */ + { 0x2f, 0x2d }, /* '/' => '-' */ + { 0x3c, 0x3b }, /* '<' => ';' */ + { 0x3e, 0x3a }, /* '>' => ':' */ + { 0x3f, 0x5f }, /* '?' => '_' */ + { 0x40, 0x22 }, /* '@' => '"' */ + { 0x5b, 0x2b }, /* '[' => '+' */ + { 0x5e, 0x26 }, /* '^' => '&' */ + { 0x5f, 0x3f }, /* '_' => '?' */ + { 0x60, 0x5c }, /* '`' => '\\' */ + { 0x7b, 0x2a }, /* '{' => '*' */ + { 0x7e, 0x7c }, /* '~' => '|' */ }; diff --git a/src/hci/keymap/keymap_ru.c b/src/hci/keymap/keymap_ru.c index 422b6c69f..c120ffd82 100644 --- a/src/hci/keymap/keymap_ru.c +++ b/src/hci/keymap/keymap_ru.c @@ -12,4 +12,5 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "ru" keyboard mapping */ struct key_mapping ru_mapping[] __keymap = { + { 0x0d, 0x0a }, /* Ctrl-M => Ctrl-J */ }; diff --git a/src/hci/keymap/keymap_sr-latin.c b/src/hci/keymap/keymap_sr-latin.c new file mode 100644 index 000000000..9d76e8a6c --- /dev/null +++ b/src/hci/keymap/keymap_sr-latin.c @@ -0,0 +1,15 @@ +/** @file + * + * "sr-latin" keyboard mapping + * + * This file is automatically generated; do not edit + * + */ + +FILE_LICENCE ( PUBLIC_DOMAIN ); + +#include + +/** "sr-latin" keyboard mapping */ +struct key_mapping sr_latin_mapping[] __keymap = { +}; diff --git a/src/hci/keymap/keymap_sr.c b/src/hci/keymap/keymap_sr.c deleted file mode 100644 index 0552f4d90..000000000 --- a/src/hci/keymap/keymap_sr.c +++ /dev/null @@ -1,35 +0,0 @@ -/** @file - * - * "sr" keyboard mapping - * - * This file is automatically generated; do not edit - * - */ - -FILE_LICENCE ( PUBLIC_DOMAIN ); - -#include - -/** "sr" keyboard mapping */ -struct key_mapping sr_mapping[] __keymap = { - { 0x19, 0x1a }, /* Ctrl-Y => Ctrl-Z */ - { 0x1a, 0x19 }, /* Ctrl-Z => Ctrl-Y */ - { 0x26, 0x2f }, /* '&' => '/' */ - { 0x28, 0x29 }, /* '(' => ')' */ - { 0x29, 0x3d }, /* ')' => '=' */ - { 0x2a, 0x28 }, /* '*' => '(' */ - { 0x2b, 0x2a }, /* '+' => '*' */ - { 0x2d, 0x27 }, /* '-' => '\'' */ - { 0x2f, 0x2d }, /* '/' => '-' */ - { 0x3c, 0x3b }, /* '<' => ';' */ - { 0x3d, 0x2b }, /* '=' => '+' */ - { 0x3e, 0x3a }, /* '>' => ':' */ - { 0x3f, 0x5f }, /* '?' => '_' */ - { 0x40, 0x22 }, /* '@' => '"' */ - { 0x59, 0x5a }, /* 'Y' => 'Z' */ - { 0x5a, 0x59 }, /* 'Z' => 'Y' */ - { 0x5e, 0x26 }, /* '^' => '&' */ - { 0x5f, 0x3f }, /* '_' => '?' */ - { 0x79, 0x7a }, /* 'y' => 'z' */ - { 0x7a, 0x79 }, /* 'z' => 'y' */ -}; diff --git a/src/hci/keymap/keymap_th.c b/src/hci/keymap/keymap_th.c deleted file mode 100644 index e8b44d1ed..000000000 --- a/src/hci/keymap/keymap_th.c +++ /dev/null @@ -1,15 +0,0 @@ -/** @file - * - * "th" keyboard mapping - * - * This file is automatically generated; do not edit - * - */ - -FILE_LICENCE ( PUBLIC_DOMAIN ); - -#include - -/** "th" keyboard mapping */ -struct key_mapping th_mapping[] __keymap = { -}; diff --git a/src/hci/keymap/keymap_wo.c b/src/hci/keymap/keymap_wo.c deleted file mode 100644 index b45357612..000000000 --- a/src/hci/keymap/keymap_wo.c +++ /dev/null @@ -1,55 +0,0 @@ -/** @file - * - * "wo" keyboard mapping - * - * This file is automatically generated; do not edit - * - */ - -FILE_LICENCE ( PUBLIC_DOMAIN ); - -#include - -/** "wo" keyboard mapping */ -struct key_mapping wo_mapping[] __keymap = { - { 0x01, 0x11 }, /* Ctrl-A => Ctrl-Q */ - { 0x11, 0x01 }, /* Ctrl-Q => Ctrl-A */ - { 0x17, 0x1a }, /* Ctrl-W => Ctrl-Z */ - { 0x1a, 0x17 }, /* Ctrl-Z => Ctrl-W */ - { 0x21, 0x31 }, /* '!' => '1' */ - { 0x23, 0x33 }, /* '#' => '3' */ - { 0x24, 0x34 }, /* '$' => '4' */ - { 0x25, 0x35 }, /* '%' => '5' */ - { 0x26, 0x37 }, /* '&' => '7' */ - { 0x28, 0x39 }, /* '(' => '9' */ - { 0x29, 0x30 }, /* ')' => '0' */ - { 0x2a, 0x38 }, /* '*' => '8' */ - { 0x2c, 0x3b }, /* ',' => ';' */ - { 0x2d, 0x29 }, /* '-' => ')' */ - { 0x2e, 0x3a }, /* '.' => ':' */ - { 0x2f, 0x21 }, /* '/' => '!' */ - { 0x31, 0x26 }, /* '1' => '&' */ - { 0x33, 0x22 }, /* '3' => '"' */ - { 0x34, 0x27 }, /* '4' => '\'' */ - { 0x35, 0x28 }, /* '5' => '(' */ - { 0x36, 0x2d }, /* '6' => '-' */ - { 0x38, 0x5f }, /* '8' => '_' */ - { 0x3a, 0x4d }, /* ':' => 'M' */ - { 0x3b, 0x6d }, /* ';' => 'm' */ - { 0x3c, 0x2e }, /* '<' => '.' */ - { 0x3e, 0x2f }, /* '>' => '/' */ - { 0x40, 0x32 }, /* '@' => '2' */ - { 0x41, 0x51 }, /* 'A' => 'Q' */ - { 0x4d, 0x3f }, /* 'M' => '?' */ - { 0x51, 0x41 }, /* 'Q' => 'A' */ - { 0x57, 0x5a }, /* 'W' => 'Z' */ - { 0x5a, 0x57 }, /* 'Z' => 'W' */ - { 0x5d, 0x24 }, /* ']' => '$' */ - { 0x5e, 0x36 }, /* '^' => '6' */ - { 0x61, 0x71 }, /* 'a' => 'q' */ - { 0x6d, 0x2c }, /* 'm' => ',' */ - { 0x71, 0x61 }, /* 'q' => 'a' */ - { 0x77, 0x7a }, /* 'w' => 'z' */ - { 0x7a, 0x77 }, /* 'z' => 'w' */ - { 0x7e, 0x25 }, /* '~' => '%' */ -}; diff --git a/src/util/genkeymap.pl b/src/util/genkeymap.pl deleted file mode 100755 index 7a5024bf8..000000000 --- a/src/util/genkeymap.pl +++ /dev/null @@ -1,238 +0,0 @@ -#!/usr/bin/perl -w -# -# Copyright (C) 2011 Michael Brown . -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of the -# License, or any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -# 02110-1301, USA. - -=head1 NAME - -genkeymap.pl - -=head1 SYNOPSIS - -genkeymap.pl [options] - -Options: - - -f,--from= Set BIOS keymap name (default "us") - -h,--help Display brief help message - -v,--verbose Increase verbosity - -q,--quiet Decrease verbosity - -=cut - -# With reference to: -# -# http://gunnarwrobel.de/wiki/Linux-and-the-keyboard.html - -use Getopt::Long; -use Pod::Usage; -use strict; -use warnings; - -use constant BIOS_KEYMAP => "us"; -use constant BKEYMAP_MAGIC => "bkeymap"; -use constant MAX_NR_KEYMAPS => 256; -use constant NR_KEYS => 128; -use constant KG_SHIFT => 0; -use constant KG_ALTGR => 1; -use constant KG_CTRL => 2; -use constant KG_ALT => 3; -use constant KG_SHIFTL => 4; -use constant KG_KANASHIFT => 4; -use constant KG_SHIFTR => 5; -use constant KG_CTRLL => 6; -use constant KG_CTRLR => 7; -use constant KG_CAPSSHIFT => 8; -use constant KT_LATIN => 0; -use constant KT_FN => 1; -use constant KT_SPEC => 2; -use constant KT_PAD => 3; -use constant KT_DEAD => 4; -use constant KT_CONS => 5; -use constant KT_CUR => 6; -use constant KT_SHIFT => 7; -use constant KT_META => 8; -use constant KT_ASCII => 9; -use constant KT_LOCK => 10; -use constant KT_LETTER => 11; -use constant KT_SLOCK => 12; -use constant KT_SPKUP => 14; - -my $verbosity = 1; -my $from_name = BIOS_KEYMAP; - -# Read named keymaps using "loadkeys -b" -# -sub read_keymaps { - my $name = shift; - my $keymaps = []; - - # Generate binary keymap - open my $pipe, "-|", "loadkeys", "-b", $name - or die "Could not load keymap \"".$name."\": $!\n"; - - # Check magic - read $pipe, my $magic, length BKEYMAP_MAGIC - or die "Could not read from \"".$name."\": $!\n"; - die "Bad magic value from \"".$name."\"\n" - unless $magic eq BKEYMAP_MAGIC; - - # Read list of included keymaps - read $pipe, my $included, MAX_NR_KEYMAPS - or die "Could not read from \"".$name."\": $!\n"; - my @included = unpack ( "C*", $included ); - die "Missing or truncated keymap list from \"".$name."\"\n" - unless @included == MAX_NR_KEYMAPS; - - # Read each keymap in turn - for ( my $keymap = 0 ; $keymap < MAX_NR_KEYMAPS ; $keymap++ ) { - if ( $included[$keymap] ) { - read $pipe, my $keysyms, ( NR_KEYS * 2 ) - or die "Could not read from \"".$name."\": $!\n"; - my @keysyms = unpack ( "S*", $keysyms ); - die "Missing or truncated keymap ".$keymap." from \"".$name."\"\n" - unless @keysyms == NR_KEYS; - push @$keymaps, \@keysyms; - } else { - push @$keymaps, undef; - } - } - - close $pipe; - return $keymaps; -} - -# Translate keysym value to ASCII -# -sub keysym_to_ascii { - my $keysym = shift; - - # Non-existent keysyms have no ASCII equivalent - return unless $keysym; - - # Sanity check - if ( $keysym & 0xf000 ) { - warn "Unexpected keysym ".sprintf ( "0x%04x", $keysym )."\n"; - return; - } - - # Extract type and value - my $type = ( $keysym >> 8 ); - my $value = ( $keysym & 0xff ); - - # Non-simple types have no ASCII equivalent - return unless ( ( $type == KT_LATIN ) || ( $type == KT_ASCII ) || - ( $type == KT_LETTER ) ); - - # High-bit-set characters cannot be generated on a US keyboard - return if $value & 0x80; - - return $value; -} - -# Translate ASCII to descriptive name -# -sub ascii_to_name { - my $ascii = shift; - - if ( $ascii == 0x5c ) { - return "'\\\\'"; - } elsif ( $ascii == 0x27 ) { - return "'\\\''"; - } elsif ( ( $ascii >= 0x20 ) && ( $ascii <= 0x7e ) ) { - return sprintf ( "'%c'", $ascii ); - } elsif ( $ascii <= 0x1a ) { - return sprintf ( "Ctrl-%c", ( 0x40 + $ascii ) ); - } else { - return sprintf ( "0x%02x", $ascii ); - } -} - -# Produce translation table between two keymaps -# -sub translate_keymaps { - my $from = shift; - my $to = shift; - my $map = {}; - - foreach my $keymap ( 0, 1 << KG_SHIFT, 1 << KG_CTRL ) { - for ( my $keycode = 0 ; $keycode < NR_KEYS ; $keycode++ ) { - my $from_ascii = keysym_to_ascii ( $from->[$keymap]->[$keycode] ) - or next; - my $to_ascii = keysym_to_ascii ( $to->[$keymap]->[$keycode] ) - or next; - my $new_map = ( ! exists $map->{$from_ascii} ); - my $update_map = - ( $new_map || ( $keycode < $map->{$from_ascii}->{keycode} ) ); - if ( ( $verbosity > 1 ) && - ( ( $from_ascii != $to_ascii ) || - ( $update_map && ! $new_map ) ) ) { - printf STDERR "In keymap %d: %s => %s%s\n", $keymap, - ascii_to_name ( $from_ascii ), ascii_to_name ( $to_ascii ), - ( $update_map ? ( $new_map ? "" : " (override)" ) - : " (ignored)" ); - } - if ( $update_map ) { - $map->{$from_ascii} = { - to_ascii => $to_ascii, - keycode => $keycode, - }; - } - } - } - return { map { $_ => $map->{$_}->{to_ascii} } keys %$map }; -} - -# Parse command-line options -Getopt::Long::Configure ( 'bundling', 'auto_abbrev' ); -GetOptions ( - 'verbose|v+' => sub { $verbosity++; }, - 'quiet|q+' => sub { $verbosity--; }, - 'from|f=s' => sub { shift; $from_name = shift; }, - 'help|h' => sub { pod2usage ( 1 ); }, -) or die "Could not parse command-line options\n"; -pod2usage ( 1 ) unless @ARGV == 1; -my $to_name = shift; - -# Read and translate keymaps -my $from = read_keymaps ( $from_name ); -my $to = read_keymaps ( $to_name ); -my $map = translate_keymaps ( $from, $to ); - -# Generate output -( my $to_name_c = $to_name ) =~ s/\W/_/g; -printf "/** \@file\n"; -printf " *\n"; -printf " * \"".$to_name."\" keyboard mapping\n"; -printf " *\n"; -printf " * This file is automatically generated; do not edit\n"; -printf " *\n"; -printf " */\n"; -printf "\n"; -printf "FILE_LICENCE ( PUBLIC_DOMAIN );\n"; -printf "\n"; -printf "#include \n"; -printf "\n"; -printf "/** \"".$to_name."\" keyboard mapping */\n"; -printf "struct key_mapping ".$to_name_c."_mapping[] __keymap = {\n"; -foreach my $from_sym ( sort { $a <=> $b } keys %$map ) { - my $to_sym = $map->{$from_sym}; - next if $from_sym == $to_sym; - printf "\t{ 0x%02x, 0x%02x },\t/* %s => %s */\n", $from_sym, $to_sym, - ascii_to_name ( $from_sym ), ascii_to_name ( $to_sym ); -} -printf "};\n"; diff --git a/src/util/genkeymap.py b/src/util/genkeymap.py new file mode 100755 index 000000000..1bb494f83 --- /dev/null +++ b/src/util/genkeymap.py @@ -0,0 +1,346 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2022 Michael Brown . +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +"""Generate iPXE keymaps""" + +from __future__ import annotations + +import argparse +from collections import UserDict +from collections.abc import Sequence, Mapping, MutableMapping +from dataclasses import dataclass +from enum import Flag, IntEnum +import re +import subprocess +from struct import Struct +import textwrap +from typing import ClassVar, Optional + + +class KeyType(IntEnum): + """Key types""" + + LATIN = 0 + FN = 1 + SPEC = 2 + PAD = 3 + DEAD = 4 + CONS = 5 + CUR = 6 + SHIFT = 7 + META = 8 + ASCII = 9 + LOCK = 10 + LETTER = 11 + SLOCK = 12 + DEAD2 = 13 + BRL = 14 + UNKNOWN = 0xf0 + + +class KeyModifiers(Flag): + """Key modifiers""" + + NONE = 0 + SHIFT = 1 + ALTGR = 2 + CTRL = 4 + ALT = 8 + SHIFTL = 16 + SHIFTR = 32 + CTRLL = 64 + CTRLR = 128 + + @property + def complexity(self) -> int: + """Get complexity value of applied modifiers""" + if self == self.NONE: + return 0 + if self == self.SHIFT: + return 1 + if self == self.CTRL: + return 2 + return 3 + bin(self.value).count('1') + + +@dataclass +class Key: + """A single key definition""" + + keycode: int + """Opaque keycode""" + + keysym: int + """Key symbol""" + + modifiers: KeyModifiers + """Applied modifiers""" + + ASCII_TYPES: ClassVar[set[KeyType]] = {KeyType.LATIN, KeyType.ASCII, + KeyType.LETTER} + """Key types with direct ASCII values""" + + @property + def keytype(self) -> Optional[KeyType]: + """Key type""" + try: + return KeyType(self.keysym >> 8) + except ValueError: + return None + + @property + def value(self) -> int: + """Key value""" + return self.keysym & 0xff + + @property + def ascii(self) -> Optional[str]: + """ASCII character""" + if self.keytype in self.ASCII_TYPES: + value = self.value + char = chr(value) + if value and char.isascii(): + return char + return None + + +class KeyMapping(UserDict[KeyModifiers, Sequence[Key]]): + """A keyboard mapping""" + + BKEYMAP_MAGIC: ClassVar[bytes] = b'bkeymap' + """Magic signature for output produced by 'loadkeys -b'""" + + MAX_NR_KEYMAPS: ClassVar[int] = 256 + """Maximum number of keymaps produced by 'loadkeys -b'""" + + NR_KEYS: ClassVar[int] = 128 + """Number of keys in each keymap produced by 'loadkeys -b'""" + + KEY_BACKSPACE: ClassVar[int] = 14 + """Key code for backspace + + Keyboard maps seem to somewhat arbitrarily pick an interpretation + for the backspace key and its various modifiers, according to the + personal preference of the keyboard map transcriber. + """ + + KEY_NON_US: ClassVar[int] = 86 + """Key code 86 + + Key code 86 is somewhat bizarre. It doesn't physically exist on + most US keyboards. The database used by "loadkeys" defines it as + "<>", while most other databases either define it as a duplicate + "\\|" or omit it entirely. + """ + + FIXUPS: ClassVar[Mapping[str, Mapping[KeyModifiers, + Sequence[tuple[int, int]]]]] = { + 'us': { + # Redefine erroneous key 86 as generating "\\|" + KeyModifiers.NONE: [(KEY_NON_US, ord('\\'))], + KeyModifiers.SHIFT: [(KEY_NON_US, ord('|'))], + # Treat Ctrl-Backspace as producing Backspace rather than Ctrl-H + KeyModifiers.CTRL: [(KEY_BACKSPACE, 0x7f)], + }, + } + """Fixups for erroneous keymappings produced by 'loadkeys -b'""" + + @property + def unshifted(self): + """Basic unshifted key mapping""" + return self[KeyModifiers.NONE] + + @property + def shifted(self): + """Basic shifted key mapping""" + return self[KeyModifiers.SHIFT] + + @classmethod + def load(cls, name: str) -> KeyMapping: + """Load keymap using 'loadkeys -b'""" + bkeymap = subprocess.check_output(["loadkeys", "-u", "-b", name]) + if not bkeymap.startswith(cls.BKEYMAP_MAGIC): + raise ValueError("Invalid bkeymap magic signature") + bkeymap = bkeymap[len(cls.BKEYMAP_MAGIC):] + included = bkeymap[:cls.MAX_NR_KEYMAPS] + if len(included) != cls.MAX_NR_KEYMAPS: + raise ValueError("Invalid bkeymap inclusion list") + keymaps = bkeymap[cls.MAX_NR_KEYMAPS:] + keys = {} + for modifiers in map(KeyModifiers, range(cls.MAX_NR_KEYMAPS)): + if included[modifiers.value]: + fmt = Struct('<%dH' % cls.NR_KEYS) + keymap = keymaps[:fmt.size] + if len(keymap) != fmt.size: + raise ValueError("Invalid bkeymap map %#x" % + modifiers.value) + keys[modifiers] = [ + Key(modifiers=modifiers, keycode=keycode, keysym=keysym) + for keycode, keysym in enumerate(fmt.unpack(keymap)) + ] + keymaps = keymaps[len(keymap):] + if keymaps: + raise ValueError("Trailing bkeymap data") + for modifiers, fixups in cls.FIXUPS.get(name, {}).items(): + for keycode, keysym in fixups: + keys[modifiers][keycode] = Key(modifiers=modifiers, + keycode=keycode, keysym=keysym) + return cls(keys) + + @property + def inverse(self) -> MutableMapping[str, Key]: + """Construct inverse mapping from ASCII value to key""" + return { + key.ascii: key + # Give priority to simplest modifier for a given ASCII code + for modifiers in sorted(self.keys(), reverse=True, + key=lambda x: (x.complexity, x.value)) + # Give priority to lowest keycode for a given ASCII code + for key in reversed(self[modifiers]) + # Ignore keys with no ASCII value + if key.ascii + } + + +class BiosKeyMapping(KeyMapping): + """Keyboard mapping as used by the BIOS""" + + @property + def inverse(self) -> MutableMapping[str, Key]: + inverse = super().inverse + assert len(inverse) == 0x7f + assert all(x.modifiers in {KeyModifiers.NONE, KeyModifiers.SHIFT, + KeyModifiers.CTRL} + for x in inverse.values()) + return inverse + + +@dataclass +class KeyRemapping: + """A keyboard remapping""" + + name: str + """Mapping name""" + + source: KeyMapping + """Source keyboard mapping""" + + target: KeyMapping + """Target keyboard mapping""" + + @property + def ascii(self) -> MutableMapping[str, str]: + """Remapped ASCII key table""" + # Construct raw mapping from source ASCII to target ASCII + raw = {source: self.target[key.modifiers][key.keycode].ascii + for source, key in self.source.inverse.items()} + # Eliminate any null mappings, mappings that attempt to remap + # the backspace key, or identity mappings + table = {source: target for source, target in raw.items() + if target + and ord(source) != 0x7f + and ord(target) != 0x7f + and ord(source) != ord(target)} + # Recursively delete any mappings that would produce + # unreachable alphanumerics (e.g. the "il" keymap, which maps + # away the whole lower-case alphabet) + while True: + unreachable = set(table.keys()) - set(table.values()) + delete = {x for x in unreachable if x.isascii() and x.isalnum()} + if not delete: + break + table = {k: v for k, v in table.items() if k not in delete} + # Sanity check: ensure that all numerics are reachable using + # the same shift state + digits = '1234567890' + unshifted = ''.join(table.get(x, x) for x in '1234567890') + shifted = ''.join(table.get(x, x) for x in '!@#$%^&*()') + if digits not in (shifted, unshifted): + raise ValueError("Inconsistent numeric remapping %s / %s" % + (unshifted, shifted)) + return dict(sorted(table.items())) + + @property + def cname(self) -> str: + """C variable name""" + return re.sub(r'\W', '_', self.name) + "_mapping" + + @staticmethod + def ascii_name(char: str) -> str: + """ASCII character name""" + if char == '\\': + name = "'\\\\'" + elif char == '\'': + name = "'\\\''" + elif char.isprintable(): + name = "'%s'" % char + elif ord(char) <= 0x1a: + name = "Ctrl-%c" % (ord(char) + 0x40) + else: + name = "0x%02x" % ord(char) + return name + + @property + def code(self) -> str: + """Generated source code""" + code = textwrap.dedent(f""" + /** @file + * + * "{self.name}" keyboard mapping + * + * This file is automatically generated; do not edit + * + */ + + FILE_LICENCE ( PUBLIC_DOMAIN ); + + #include + + /** "{self.name}" keyboard mapping */ + struct key_mapping {self.cname}[] __keymap = {{ + """).lstrip() + ''.join( + '\t{ 0x%02x, 0x%02x },\t/* %s => %s */\n' % ( + ord(source), ord(target), + self.ascii_name(source), self.ascii_name(target) + ) + for source, target in self.ascii.items() + ) + textwrap.dedent(""" + }; + """).strip() + return code + + +if __name__ == '__main__': + + # Parse command-line arguments + parser = argparse.ArgumentParser(description="Generate iPXE keymaps") + parser.add_argument('--verbose', '-v', action='count', default=0, + help="Increase verbosity") + parser.add_argument('layout', help="Target keyboard layout") + args = parser.parse_args() + + # Load source and target keymaps + source = BiosKeyMapping.load('us') + target = KeyMapping.load(args.layout) + + # Construct remapping + remap = KeyRemapping(name=args.layout, source=source, target=target) + + # Output generated code + print(remap.code) -- cgit v1.2.3-55-g7522 From 0bbd8967830097b9141945ba960e90339c230ccb Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 9 Feb 2022 15:43:42 +0000 Subject: [console] Handle remapping of scancode 86 The key with scancode 86 appears in the position between left shift and Z on a US keyboard, where it typically fails to exist entirely. Most US keyboard maps define this nonexistent key as generating "\|", with the notable exception of "loadkeys" which instead reports it as generating "<>". Both of these mapping choices duplicate keys that exist elsewhere in the map, which causes problems for our ASCII-based remapping mechanism. Work around these quirks by treating the key as generating "\|" with the high bit set, and making it subject to remapping. Where the BIOS generates "\|" as expected, this allows us to remap to the correct ASCII value. Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/bios_console.c | 9 ++++++++ src/core/keymap.c | 3 +++ src/drivers/usb/usbkbd.c | 8 ++++++- src/drivers/usb/usbkbd.h | 1 + src/hci/keymap/keymap_al.c | 2 ++ src/hci/keymap/keymap_az.c | 2 ++ src/hci/keymap/keymap_by.c | 2 ++ src/hci/keymap/keymap_de.c | 2 ++ src/hci/keymap/keymap_dk.c | 2 ++ src/hci/keymap/keymap_es.c | 2 ++ src/hci/keymap/keymap_et.c | 2 ++ src/hci/keymap/keymap_fi.c | 2 ++ src/hci/keymap/keymap_fr.c | 2 ++ src/hci/keymap/keymap_gr.c | 2 ++ src/hci/keymap/keymap_il.c | 2 ++ src/hci/keymap/keymap_it.c | 2 ++ src/hci/keymap/keymap_mk.c | 2 ++ src/hci/keymap/keymap_nl.c | 2 ++ src/hci/keymap/keymap_no-latin1.c | 2 ++ src/hci/keymap/keymap_no.c | 2 ++ src/hci/keymap/keymap_pl.c | 2 ++ src/hci/keymap/keymap_pt.c | 2 ++ src/hci/keymap/keymap_ru.c | 2 ++ src/hci/keymap/keymap_sg.c | 2 ++ src/hci/keymap/keymap_sr-latin.c | 2 ++ src/hci/keymap/keymap_ua.c | 2 ++ src/include/ipxe/keymap.h | 3 +++ src/util/genkeymap.py | 31 +++++++++++++++++++++++----- 28 files changed, 93 insertions(+), 6 deletions(-) diff --git a/src/arch/x86/interface/pcbios/bios_console.c b/src/arch/x86/interface/pcbios/bios_console.c index 443513e9e..438a01d07 100644 --- a/src/arch/x86/interface/pcbios/bios_console.c +++ b/src/arch/x86/interface/pcbios/bios_console.c @@ -68,6 +68,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define SCANCODE_RSHIFT 0x36 +/** Scancode for the "non-US \ and |" key + * + * This is the key that appears between Left Shift and Z on non-US + * keyboards. + */ +#define SCANCODE_NON_US 0x56 + /* Set default console usage if applicable */ #if ! ( defined ( CONSOLE_PCBIOS ) && CONSOLE_EXPLICIT ( CONSOLE_PCBIOS ) ) #undef CONSOLE_PCBIOS @@ -383,6 +390,8 @@ static int bios_getchar ( void ) { if ( character && ( character < 0x80 ) ) { if ( scancode < SCANCODE_RSHIFT ) { return key_remap ( character ); + } else if ( scancode == SCANCODE_NON_US ) { + return key_remap ( character | KEYMAP_PSEUDO ); } else { return character; } diff --git a/src/core/keymap.c b/src/core/keymap.c index a6707a2ce..5054e4769 100644 --- a/src/core/keymap.c +++ b/src/core/keymap.c @@ -48,5 +48,8 @@ unsigned int key_remap ( unsigned int character ) { } } + /* Clear pseudo key flag */ + character &= ~KEYMAP_PSEUDO; + return character; } diff --git a/src/drivers/usb/usbkbd.c b/src/drivers/usb/usbkbd.c index ba4b2d4d7..6954cd69b 100644 --- a/src/drivers/usb/usbkbd.c +++ b/src/drivers/usb/usbkbd.c @@ -114,13 +114,19 @@ static unsigned int usbkbd_map ( unsigned int keycode, unsigned int modifiers, }; key = keypad[ keycode - USBKBD_KEY_PAD_1 ]; }; + } else if ( keycode == USBKBD_KEY_NON_US ) { + /* Non-US \ and | */ + key = ( ( modifiers & USBKBD_SHIFT ) ? + ( KEYMAP_PSEUDO | '|' ) : ( KEYMAP_PSEUDO | '\\' ) ); } else { key = 0; } /* Remap key if applicable */ - if ( keycode < USBKBD_KEY_CAPS_LOCK ) + if ( ( keycode < USBKBD_KEY_CAPS_LOCK ) || + ( keycode == USBKBD_KEY_NON_US ) ) { key = key_remap ( key ); + } /* Handle upper/lower case and Ctrl- */ if ( islower ( key ) ) { diff --git a/src/drivers/usb/usbkbd.h b/src/drivers/usb/usbkbd.h index cedebfe71..1a3fea1ba 100644 --- a/src/drivers/usb/usbkbd.h +++ b/src/drivers/usb/usbkbd.h @@ -75,6 +75,7 @@ enum usb_keycode { USBKBD_KEY_PAD_ENTER = 0x58, USBKBD_KEY_PAD_1 = 0x59, USBKBD_KEY_PAD_DOT = 0x63, + USBKBD_KEY_NON_US = 0x64, }; /** USB keyboard LEDs */ diff --git a/src/hci/keymap/keymap_al.c b/src/hci/keymap/keymap_al.c index e4418361b..6b4663489 100644 --- a/src/hci/keymap/keymap_al.c +++ b/src/hci/keymap/keymap_al.c @@ -30,4 +30,6 @@ struct key_mapping al_mapping[] __keymap = { { 0x7c, 0x7d }, /* '|' => '}' */ { 0x7d, 0x27 }, /* '}' => '\'' */ { 0x7e, 0x7c }, /* '~' => '|' */ + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_az.c b/src/hci/keymap/keymap_az.c index 525ab2336..91a243460 100644 --- a/src/hci/keymap/keymap_az.c +++ b/src/hci/keymap/keymap_az.c @@ -21,4 +21,6 @@ struct key_mapping az_mapping[] __keymap = { { 0x40, 0x22 }, /* '@' => '"' */ { 0x5e, 0x3a }, /* '^' => ':' */ { 0x7c, 0x2f }, /* '|' => '/' */ + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_by.c b/src/hci/keymap/keymap_by.c index 514d0b532..43fb746bf 100644 --- a/src/hci/keymap/keymap_by.c +++ b/src/hci/keymap/keymap_by.c @@ -12,4 +12,6 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "by" keyboard mapping */ struct key_mapping by_mapping[] __keymap = { + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_de.c b/src/hci/keymap/keymap_de.c index 2559e1538..85574d487 100644 --- a/src/hci/keymap/keymap_de.c +++ b/src/hci/keymap/keymap_de.c @@ -36,4 +36,6 @@ struct key_mapping de_mapping[] __keymap = { { 0x7a, 0x79 }, /* 'z' => 'y' */ { 0x7c, 0x27 }, /* '|' => '\'' */ { 0x7d, 0x2a }, /* '}' => '*' */ + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_dk.c b/src/hci/keymap/keymap_dk.c index 05110dc89..4e1d5a739 100644 --- a/src/hci/keymap/keymap_dk.c +++ b/src/hci/keymap/keymap_dk.c @@ -28,4 +28,6 @@ struct key_mapping dk_mapping[] __keymap = { { 0x5e, 0x26 }, /* '^' => '&' */ { 0x5f, 0x3f }, /* '_' => '?' */ { 0x7c, 0x2a }, /* '|' => '*' */ + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_es.c b/src/hci/keymap/keymap_es.c index 51dedfff7..91327ea51 100644 --- a/src/hci/keymap/keymap_es.c +++ b/src/hci/keymap/keymap_es.c @@ -28,4 +28,6 @@ struct key_mapping es_mapping[] __keymap = { { 0x5e, 0x26 }, /* '^' => '&' */ { 0x5f, 0x3f }, /* '_' => '?' */ { 0x7d, 0x2a }, /* '}' => '*' */ + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_et.c b/src/hci/keymap/keymap_et.c index dd0f879b1..493ec93d4 100644 --- a/src/hci/keymap/keymap_et.c +++ b/src/hci/keymap/keymap_et.c @@ -26,4 +26,6 @@ struct key_mapping et_mapping[] __keymap = { { 0x5e, 0x26 }, /* '^' => '&' */ { 0x5f, 0x3f }, /* '_' => '?' */ { 0x7c, 0x2a }, /* '|' => '*' */ + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_fi.c b/src/hci/keymap/keymap_fi.c index c489bf0e5..18f48d47e 100644 --- a/src/hci/keymap/keymap_fi.c +++ b/src/hci/keymap/keymap_fi.c @@ -26,4 +26,6 @@ struct key_mapping fi_mapping[] __keymap = { { 0x5e, 0x26 }, /* '^' => '&' */ { 0x5f, 0x3f }, /* '_' => '?' */ { 0x7c, 0x2a }, /* '|' => '*' */ + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_fr.c b/src/hci/keymap/keymap_fr.c index 8f3b4999d..808cd7945 100644 --- a/src/hci/keymap/keymap_fr.c +++ b/src/hci/keymap/keymap_fr.c @@ -57,4 +57,6 @@ struct key_mapping fr_mapping[] __keymap = { { 0x71, 0x61 }, /* 'q' => 'a' */ { 0x77, 0x7a }, /* 'w' => 'z' */ { 0x7a, 0x77 }, /* 'z' => 'w' */ + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_gr.c b/src/hci/keymap/keymap_gr.c index 42b6418e8..b48142e5e 100644 --- a/src/hci/keymap/keymap_gr.c +++ b/src/hci/keymap/keymap_gr.c @@ -12,4 +12,6 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "gr" keyboard mapping */ struct key_mapping gr_mapping[] __keymap = { + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_il.c b/src/hci/keymap/keymap_il.c index f631f7ac9..78e7fa970 100644 --- a/src/hci/keymap/keymap_il.c +++ b/src/hci/keymap/keymap_il.c @@ -24,4 +24,6 @@ struct key_mapping il_mapping[] __keymap = { { 0x60, 0x3b }, /* '`' => ';' */ { 0x7b, 0x7d }, /* '{' => '}' */ { 0x7d, 0x7b }, /* '}' => '{' */ + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_it.c b/src/hci/keymap/keymap_it.c index d96102c9e..5a8e2b38d 100644 --- a/src/hci/keymap/keymap_it.c +++ b/src/hci/keymap/keymap_it.c @@ -30,4 +30,6 @@ struct key_mapping it_mapping[] __keymap = { { 0x60, 0x5c }, /* '`' => '\\' */ { 0x7d, 0x2a }, /* '}' => '*' */ { 0x7e, 0x7c }, /* '~' => '|' */ + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_mk.c b/src/hci/keymap/keymap_mk.c index 8f5060778..9f2cff78b 100644 --- a/src/hci/keymap/keymap_mk.c +++ b/src/hci/keymap/keymap_mk.c @@ -12,4 +12,6 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "mk" keyboard mapping */ struct key_mapping mk_mapping[] __keymap = { + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_nl.c b/src/hci/keymap/keymap_nl.c index 2a0fbbcbd..d248fc8ae 100644 --- a/src/hci/keymap/keymap_nl.c +++ b/src/hci/keymap/keymap_nl.c @@ -33,4 +33,6 @@ struct key_mapping nl_mapping[] __keymap = { { 0x60, 0x40 }, /* '`' => '@' */ { 0x7c, 0x3e }, /* '|' => '>' */ { 0x7d, 0x7c }, /* '}' => '|' */ + { 0xdc, 0x5d }, /* Pseudo-'\\' => ']' */ + { 0xfc, 0x5b }, /* Pseudo-'|' => '[' */ }; diff --git a/src/hci/keymap/keymap_no-latin1.c b/src/hci/keymap/keymap_no-latin1.c index 655e4cef7..d5a721a90 100644 --- a/src/hci/keymap/keymap_no-latin1.c +++ b/src/hci/keymap/keymap_no-latin1.c @@ -32,4 +32,6 @@ struct key_mapping no_latin1_mapping[] __keymap = { { 0x60, 0x7c }, /* '`' => '|' */ { 0x7c, 0x2a }, /* '|' => '*' */ { 0x7d, 0x5e }, /* '}' => '^' */ + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_no.c b/src/hci/keymap/keymap_no.c index 7a2df7c5a..b6190da4a 100644 --- a/src/hci/keymap/keymap_no.c +++ b/src/hci/keymap/keymap_no.c @@ -30,4 +30,6 @@ struct key_mapping no_mapping[] __keymap = { { 0x5f, 0x3f }, /* '_' => '?' */ { 0x60, 0x7c }, /* '`' => '|' */ { 0x7c, 0x2a }, /* '|' => '*' */ + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_pl.c b/src/hci/keymap/keymap_pl.c index 51822e072..224fbde28 100644 --- a/src/hci/keymap/keymap_pl.c +++ b/src/hci/keymap/keymap_pl.c @@ -12,4 +12,6 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "pl" keyboard mapping */ struct key_mapping pl_mapping[] __keymap = { + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_pt.c b/src/hci/keymap/keymap_pt.c index b993902af..6d850fee8 100644 --- a/src/hci/keymap/keymap_pt.c +++ b/src/hci/keymap/keymap_pt.c @@ -29,4 +29,6 @@ struct key_mapping pt_mapping[] __keymap = { { 0x60, 0x5c }, /* '`' => '\\' */ { 0x7b, 0x2a }, /* '{' => '*' */ { 0x7e, 0x7c }, /* '~' => '|' */ + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_ru.c b/src/hci/keymap/keymap_ru.c index c120ffd82..f7611c30a 100644 --- a/src/hci/keymap/keymap_ru.c +++ b/src/hci/keymap/keymap_ru.c @@ -13,4 +13,6 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "ru" keyboard mapping */ struct key_mapping ru_mapping[] __keymap = { { 0x0d, 0x0a }, /* Ctrl-M => Ctrl-J */ + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_sg.c b/src/hci/keymap/keymap_sg.c index 0b0820929..9a515c745 100644 --- a/src/hci/keymap/keymap_sg.c +++ b/src/hci/keymap/keymap_sg.c @@ -38,4 +38,6 @@ struct key_mapping sg_mapping[] __keymap = { { 0x7a, 0x79 }, /* 'z' => 'y' */ { 0x7c, 0x24 }, /* '|' => '$' */ { 0x7d, 0x21 }, /* '}' => '!' */ + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_sr-latin.c b/src/hci/keymap/keymap_sr-latin.c index 9d76e8a6c..1d4588733 100644 --- a/src/hci/keymap/keymap_sr-latin.c +++ b/src/hci/keymap/keymap_sr-latin.c @@ -12,4 +12,6 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "sr-latin" keyboard mapping */ struct key_mapping sr_latin_mapping[] __keymap = { + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/hci/keymap/keymap_ua.c b/src/hci/keymap/keymap_ua.c index 1106a8b28..50f2e184d 100644 --- a/src/hci/keymap/keymap_ua.c +++ b/src/hci/keymap/keymap_ua.c @@ -12,4 +12,6 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "ua" keyboard mapping */ struct key_mapping ua_mapping[] __keymap = { + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ }; diff --git a/src/include/ipxe/keymap.h b/src/include/ipxe/keymap.h index 62b3bb131..93c9e7314 100644 --- a/src/include/ipxe/keymap.h +++ b/src/include/ipxe/keymap.h @@ -27,6 +27,9 @@ struct key_mapping { /** Define a keyboard mapping */ #define __keymap __table_entry ( KEYMAP, 01 ) +/** Pseudo key flag */ +#define KEYMAP_PSEUDO 0x80 + extern unsigned int key_remap ( unsigned int character ); #endif /* _IPXE_KEYMAP_H */ diff --git a/src/util/genkeymap.py b/src/util/genkeymap.py index 1bb494f83..081e314cc 100755 --- a/src/util/genkeymap.py +++ b/src/util/genkeymap.py @@ -219,12 +219,28 @@ class KeyMapping(UserDict[KeyModifiers, Sequence[Key]]): class BiosKeyMapping(KeyMapping): - """Keyboard mapping as used by the BIOS""" + """Keyboard mapping as used by the BIOS + + To allow for remappings of the somewhat interesting key 86, we + arrange for our keyboard drivers to generate this key as "\\|" + with the high bit set. + """ + + KEY_PSEUDO: ClassVar[int] = 0x80 + """Flag used to indicate a fake ASCII value""" + + KEY_NON_US_UNSHIFTED: ClassVar[str] = chr(KEY_PSEUDO | ord('\\')) + """Fake ASCII value generated for unshifted key code 86""" + + KEY_NON_US_SHIFTED: ClassVar[str] = chr(KEY_PSEUDO | ord('|')) + """Fake ASCII value generated for shifted key code 86""" @property def inverse(self) -> MutableMapping[str, Key]: inverse = super().inverse assert len(inverse) == 0x7f + inverse[self.KEY_NON_US_UNSHIFTED] = self.unshifted[self.KEY_NON_US] + inverse[self.KEY_NON_US_SHIFTED] = self.shifted[self.KEY_NON_US] assert all(x.modifiers in {KeyModifiers.NONE, KeyModifiers.SHIFT, KeyModifiers.CTRL} for x in inverse.values()) @@ -251,12 +267,13 @@ class KeyRemapping: raw = {source: self.target[key.modifiers][key.keycode].ascii for source, key in self.source.inverse.items()} # Eliminate any null mappings, mappings that attempt to remap - # the backspace key, or identity mappings + # the backspace key, or mappings that would become identity + # mappings after clearing the high bit table = {source: target for source, target in raw.items() if target and ord(source) != 0x7f and ord(target) != 0x7f - and ord(source) != ord(target)} + and ord(source) & ~BiosKeyMapping.KEY_PSEUDO != ord(target)} # Recursively delete any mappings that would produce # unreachable alphanumerics (e.g. the "il" keymap, which maps # away the whole lower-case alphabet) @@ -281,13 +298,17 @@ class KeyRemapping: """C variable name""" return re.sub(r'\W', '_', self.name) + "_mapping" - @staticmethod - def ascii_name(char: str) -> str: + @classmethod + def ascii_name(cls, char: str) -> str: """ASCII character name""" if char == '\\': name = "'\\\\'" elif char == '\'': name = "'\\\''" + elif ord(char) & BiosKeyMapping.KEY_PSEUDO: + name = "Pseudo-%s" % cls.ascii_name( + chr(ord(char) & ~BiosKeyMapping.KEY_PSEUDO) + ) elif char.isprintable(): name = "'%s'" % char elif ord(char) <= 0x1a: -- cgit v1.2.3-55-g7522 From 1150321595c44acfac2b2c56590d4d7f1d2ad70c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 14 Feb 2022 13:13:37 +0000 Subject: [tables] Add ability to declare static table start and end markers The compound statement expression within __table_entries() prevents the use of top-level declarations such as static struct thing *things = table_start ( THINGS ); Define TABLE_START() and TABLE_END() macros that can be used as: static TABLE_START ( things_start, THINGS ); static struct thing *things = things_start; Signed-off-by: Michael Brown --- src/include/ipxe/tables.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/include/ipxe/tables.h b/src/include/ipxe/tables.h index 28a87da96..de5b1f297 100644 --- a/src/include/ipxe/tables.h +++ b/src/include/ipxe/tables.h @@ -252,6 +252,17 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); __attribute__ (( unused )); \ __table_entries; } ) +/** + * Declare start of linker table entries + * + * @v entries Start of entries + * @v table Linker table + * @v idx Sub-table index + */ +#define __TABLE_ENTRIES( entries, table, idx ) \ + __table_type ( table ) entries[0] \ + __table_entry ( table, idx ) + /** * Get start of linker table * @@ -270,6 +281,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define table_start( table ) __table_entries ( table, 00 ) +/** + * Declare start of linker table + * + * @v start Start of linker table + * @v table Linker table + */ +#define TABLE_START( start, table ) __TABLE_ENTRIES ( start, table, 00 ) + /** * Get end of linker table * @@ -288,6 +307,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #define table_end( table ) __table_entries ( table, 99 ) +/** + * Declare end of linker table + * + * @v end End of linker table + * @v table Linker table + */ +#define TABLE_END( end, table ) __TABLE_ENTRIES ( end, table, 99 ) + /** * Get number of entries in linker table * -- cgit v1.2.3-55-g7522 From 871dd236d4aff66e871c25addcf522fe75a4ccd7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 14 Feb 2022 13:22:48 +0000 Subject: [console] Allow for named keyboard mappings Separate the concept of a keyboard mapping from a list of remapped keys, to allow for the possibility of supporting multiple keyboard mappings at runtime. Signed-off-by: Michael Brown --- src/core/keymap.c | 14 ++-- src/hci/keymap/keymap_al.c | 11 ++- src/hci/keymap/keymap_az.c | 11 ++- src/hci/keymap/keymap_by.c | 11 ++- src/hci/keymap/keymap_cf.c | 11 ++- src/hci/keymap/keymap_cz.c | 11 ++- src/hci/keymap/keymap_de.c | 11 ++- src/hci/keymap/keymap_dk.c | 11 ++- src/hci/keymap/keymap_es.c | 11 ++- src/hci/keymap/keymap_et.c | 11 ++- src/hci/keymap/keymap_fi.c | 11 ++- src/hci/keymap/keymap_fr.c | 11 ++- src/hci/keymap/keymap_gr.c | 11 ++- src/hci/keymap/keymap_hu.c | 11 ++- src/hci/keymap/keymap_il.c | 11 ++- src/hci/keymap/keymap_it.c | 11 ++- src/hci/keymap/keymap_lt.c | 11 ++- src/hci/keymap/keymap_mk.c | 11 ++- src/hci/keymap/keymap_mt.c | 11 ++- src/hci/keymap/keymap_nl.c | 11 ++- src/hci/keymap/keymap_no-latin1.c | 11 ++- src/hci/keymap/keymap_no.c | 11 ++- src/hci/keymap/keymap_pl.c | 11 ++- src/hci/keymap/keymap_pt.c | 11 ++- src/hci/keymap/keymap_ro.c | 11 ++- src/hci/keymap/keymap_ru.c | 11 ++- src/hci/keymap/keymap_sg.c | 11 ++- src/hci/keymap/keymap_sr-latin.c | 11 ++- src/hci/keymap/keymap_ua.c | 11 ++- src/hci/keymap/keymap_uk.c | 11 ++- src/hci/keymap/keymap_us.c | 11 ++- src/include/ipxe/keymap.h | 19 +++++- src/util/genkeymap.py | 140 +++++++++++++++++++++----------------- 33 files changed, 373 insertions(+), 130 deletions(-) diff --git a/src/core/keymap.c b/src/core/keymap.c index 5054e4769..c0953967a 100644 --- a/src/core/keymap.c +++ b/src/core/keymap.c @@ -31,6 +31,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +/** Default keyboard mapping */ +static TABLE_START ( keymap_start, KEYMAP ); + +/** Current keyboard mapping */ +static struct keymap *keymap = keymap_start; + /** * Remap a key * @@ -38,12 +44,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @ret character Mapped character */ unsigned int key_remap ( unsigned int character ) { - struct key_mapping *mapping; + struct keymap_key *key; /* Remap via table */ - for_each_table_entry ( mapping, KEYMAP ) { - if ( mapping->from == character ) { - character = mapping->to; + for ( key = keymap->basic ; key->from ; key++ ) { + if ( key->from == character ) { + character = key->to; break; } } diff --git a/src/hci/keymap/keymap_al.c b/src/hci/keymap/keymap_al.c index 6b4663489..a3df385a9 100644 --- a/src/hci/keymap/keymap_al.c +++ b/src/hci/keymap/keymap_al.c @@ -10,8 +10,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "al" keyboard mapping */ -struct key_mapping al_mapping[] __keymap = { +/** "al" basic remapping */ +static struct keymap_key al_basic[] = { { 0x19, 0x1a }, /* Ctrl-Y => Ctrl-Z */ { 0x1a, 0x19 }, /* Ctrl-Z => Ctrl-Y */ { 0x1c, 0x1d }, /* 0x1c => 0x1d */ @@ -32,4 +32,11 @@ struct key_mapping al_mapping[] __keymap = { { 0x7e, 0x7c }, /* '~' => '|' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "al" keyboard map */ +struct keymap al_keymap __keymap = { + .name = "al", + .basic = al_basic, }; diff --git a/src/hci/keymap/keymap_az.c b/src/hci/keymap/keymap_az.c index 91a243460..7b382ca8b 100644 --- a/src/hci/keymap/keymap_az.c +++ b/src/hci/keymap/keymap_az.c @@ -10,8 +10,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "az" keyboard mapping */ -struct key_mapping az_mapping[] __keymap = { +/** "az" basic remapping */ +static struct keymap_key az_basic[] = { { 0x1e, 0x36 }, /* 0x1e => '6' */ { 0x24, 0x3b }, /* '$' => ';' */ { 0x26, 0x3f }, /* '&' => '?' */ @@ -23,4 +23,11 @@ struct key_mapping az_mapping[] __keymap = { { 0x7c, 0x2f }, /* '|' => '/' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "az" keyboard map */ +struct keymap az_keymap __keymap = { + .name = "az", + .basic = az_basic, }; diff --git a/src/hci/keymap/keymap_by.c b/src/hci/keymap/keymap_by.c index 43fb746bf..4127609e3 100644 --- a/src/hci/keymap/keymap_by.c +++ b/src/hci/keymap/keymap_by.c @@ -10,8 +10,15 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "by" keyboard mapping */ -struct key_mapping by_mapping[] __keymap = { +/** "by" basic remapping */ +static struct keymap_key by_basic[] = { { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "by" keyboard map */ +struct keymap by_keymap __keymap = { + .name = "by", + .basic = by_basic, }; diff --git a/src/hci/keymap/keymap_cf.c b/src/hci/keymap/keymap_cf.c index d7e63b9b1..0bbe89659 100644 --- a/src/hci/keymap/keymap_cf.c +++ b/src/hci/keymap/keymap_cf.c @@ -10,8 +10,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "cf" keyboard mapping */ -struct key_mapping cf_mapping[] __keymap = { +/** "cf" basic remapping */ +static struct keymap_key cf_basic[] = { { 0x23, 0x2f }, /* '#' => '/' */ { 0x3c, 0x27 }, /* '<' => '\'' */ { 0x3e, 0x2e }, /* '>' => '.' */ @@ -21,4 +21,11 @@ struct key_mapping cf_mapping[] __keymap = { { 0x60, 0x23 }, /* '`' => '#' */ { 0x7c, 0x3e }, /* '|' => '>' */ { 0x7e, 0x7c }, /* '~' => '|' */ + { 0, 0 } +}; + +/** "cf" keyboard map */ +struct keymap cf_keymap __keymap = { + .name = "cf", + .basic = cf_basic, }; diff --git a/src/hci/keymap/keymap_cz.c b/src/hci/keymap/keymap_cz.c index 2b4a21592..8655d5b68 100644 --- a/src/hci/keymap/keymap_cz.c +++ b/src/hci/keymap/keymap_cz.c @@ -10,8 +10,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "cz" keyboard mapping */ -struct key_mapping cz_mapping[] __keymap = { +/** "cz" basic remapping */ +static struct keymap_key cz_basic[] = { { 0x19, 0x1a }, /* Ctrl-Y => Ctrl-Z */ { 0x1a, 0x19 }, /* Ctrl-Z => Ctrl-Y */ { 0x1f, 0x1c }, /* 0x1f => 0x1c */ @@ -43,4 +43,11 @@ struct key_mapping cz_mapping[] __keymap = { { 0x7b, 0x2f }, /* '{' => '/' */ { 0x7c, 0x27 }, /* '|' => '\'' */ { 0x7d, 0x28 }, /* '}' => '(' */ + { 0, 0 } +}; + +/** "cz" keyboard map */ +struct keymap cz_keymap __keymap = { + .name = "cz", + .basic = cz_basic, }; diff --git a/src/hci/keymap/keymap_de.c b/src/hci/keymap/keymap_de.c index 85574d487..4d23c2e60 100644 --- a/src/hci/keymap/keymap_de.c +++ b/src/hci/keymap/keymap_de.c @@ -10,8 +10,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "de" keyboard mapping */ -struct key_mapping de_mapping[] __keymap = { +/** "de" basic remapping */ +static struct keymap_key de_basic[] = { { 0x19, 0x1a }, /* Ctrl-Y => Ctrl-Z */ { 0x1a, 0x19 }, /* Ctrl-Z => Ctrl-Y */ { 0x1c, 0x23 }, /* 0x1c => '#' */ @@ -38,4 +38,11 @@ struct key_mapping de_mapping[] __keymap = { { 0x7d, 0x2a }, /* '}' => '*' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "de" keyboard map */ +struct keymap de_keymap __keymap = { + .name = "de", + .basic = de_basic, }; diff --git a/src/hci/keymap/keymap_dk.c b/src/hci/keymap/keymap_dk.c index 4e1d5a739..100246bf5 100644 --- a/src/hci/keymap/keymap_dk.c +++ b/src/hci/keymap/keymap_dk.c @@ -10,8 +10,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "dk" keyboard mapping */ -struct key_mapping dk_mapping[] __keymap = { +/** "dk" basic remapping */ +static struct keymap_key dk_basic[] = { { 0x1c, 0x27 }, /* 0x1c => '\'' */ { 0x1e, 0x36 }, /* 0x1e => '6' */ { 0x26, 0x2f }, /* '&' => '/' */ @@ -30,4 +30,11 @@ struct key_mapping dk_mapping[] __keymap = { { 0x7c, 0x2a }, /* '|' => '*' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "dk" keyboard map */ +struct keymap dk_keymap __keymap = { + .name = "dk", + .basic = dk_basic, }; diff --git a/src/hci/keymap/keymap_es.c b/src/hci/keymap/keymap_es.c index 91327ea51..2f4b86c47 100644 --- a/src/hci/keymap/keymap_es.c +++ b/src/hci/keymap/keymap_es.c @@ -10,8 +10,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "es" keyboard mapping */ -struct key_mapping es_mapping[] __keymap = { +/** "es" basic remapping */ +static struct keymap_key es_basic[] = { { 0x1c, 0x1d }, /* 0x1c => 0x1d */ { 0x1e, 0x36 }, /* 0x1e => '6' */ { 0x26, 0x2f }, /* '&' => '/' */ @@ -30,4 +30,11 @@ struct key_mapping es_mapping[] __keymap = { { 0x7d, 0x2a }, /* '}' => '*' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "es" keyboard map */ +struct keymap es_keymap __keymap = { + .name = "es", + .basic = es_basic, }; diff --git a/src/hci/keymap/keymap_et.c b/src/hci/keymap/keymap_et.c index 493ec93d4..a8bf46ebc 100644 --- a/src/hci/keymap/keymap_et.c +++ b/src/hci/keymap/keymap_et.c @@ -10,8 +10,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "et" keyboard mapping */ -struct key_mapping et_mapping[] __keymap = { +/** "et" basic remapping */ +static struct keymap_key et_basic[] = { { 0x26, 0x2f }, /* '&' => '/' */ { 0x28, 0x29 }, /* '(' => ')' */ { 0x29, 0x3d }, /* ')' => '=' */ @@ -28,4 +28,11 @@ struct key_mapping et_mapping[] __keymap = { { 0x7c, 0x2a }, /* '|' => '*' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "et" keyboard map */ +struct keymap et_keymap __keymap = { + .name = "et", + .basic = et_basic, }; diff --git a/src/hci/keymap/keymap_fi.c b/src/hci/keymap/keymap_fi.c index 18f48d47e..eb75eb4dc 100644 --- a/src/hci/keymap/keymap_fi.c +++ b/src/hci/keymap/keymap_fi.c @@ -10,8 +10,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "fi" keyboard mapping */ -struct key_mapping fi_mapping[] __keymap = { +/** "fi" basic remapping */ +static struct keymap_key fi_basic[] = { { 0x26, 0x2f }, /* '&' => '/' */ { 0x28, 0x29 }, /* '(' => ')' */ { 0x29, 0x3d }, /* ')' => '=' */ @@ -28,4 +28,11 @@ struct key_mapping fi_mapping[] __keymap = { { 0x7c, 0x2a }, /* '|' => '*' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "fi" keyboard map */ +struct keymap fi_keymap __keymap = { + .name = "fi", + .basic = fi_basic, }; diff --git a/src/hci/keymap/keymap_fr.c b/src/hci/keymap/keymap_fr.c index 808cd7945..523254ee5 100644 --- a/src/hci/keymap/keymap_fr.c +++ b/src/hci/keymap/keymap_fr.c @@ -10,8 +10,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "fr" keyboard mapping */ -struct key_mapping fr_mapping[] __keymap = { +/** "fr" basic remapping */ +static struct keymap_key fr_basic[] = { { 0x01, 0x11 }, /* Ctrl-A => Ctrl-Q */ { 0x11, 0x01 }, /* Ctrl-Q => Ctrl-A */ { 0x17, 0x1a }, /* Ctrl-W => Ctrl-Z */ @@ -59,4 +59,11 @@ struct key_mapping fr_mapping[] __keymap = { { 0x7a, 0x77 }, /* 'z' => 'w' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "fr" keyboard map */ +struct keymap fr_keymap __keymap = { + .name = "fr", + .basic = fr_basic, }; diff --git a/src/hci/keymap/keymap_gr.c b/src/hci/keymap/keymap_gr.c index b48142e5e..16a2a7032 100644 --- a/src/hci/keymap/keymap_gr.c +++ b/src/hci/keymap/keymap_gr.c @@ -10,8 +10,15 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "gr" keyboard mapping */ -struct key_mapping gr_mapping[] __keymap = { +/** "gr" basic remapping */ +static struct keymap_key gr_basic[] = { { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "gr" keyboard map */ +struct keymap gr_keymap __keymap = { + .name = "gr", + .basic = gr_basic, }; diff --git a/src/hci/keymap/keymap_hu.c b/src/hci/keymap/keymap_hu.c index a2eadbc62..5e407161f 100644 --- a/src/hci/keymap/keymap_hu.c +++ b/src/hci/keymap/keymap_hu.c @@ -10,8 +10,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "hu" keyboard mapping */ -struct key_mapping hu_mapping[] __keymap = { +/** "hu" basic remapping */ +static struct keymap_key hu_basic[] = { { 0x19, 0x1a }, /* Ctrl-Y => Ctrl-Z */ { 0x1a, 0x19 }, /* Ctrl-Z => Ctrl-Y */ { 0x1e, 0x36 }, /* 0x1e => '6' */ @@ -32,4 +32,11 @@ struct key_mapping hu_mapping[] __keymap = { { 0x60, 0x30 }, /* '`' => '0' */ { 0x79, 0x7a }, /* 'y' => 'z' */ { 0x7a, 0x79 }, /* 'z' => 'y' */ + { 0, 0 } +}; + +/** "hu" keyboard map */ +struct keymap hu_keymap __keymap = { + .name = "hu", + .basic = hu_basic, }; diff --git a/src/hci/keymap/keymap_il.c b/src/hci/keymap/keymap_il.c index 78e7fa970..de5e639ca 100644 --- a/src/hci/keymap/keymap_il.c +++ b/src/hci/keymap/keymap_il.c @@ -10,8 +10,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "il" keyboard mapping */ -struct key_mapping il_mapping[] __keymap = { +/** "il" basic remapping */ +static struct keymap_key il_basic[] = { { 0x1d, 0x1b }, /* 0x1d => 0x1b */ { 0x27, 0x2c }, /* '\'' => ',' */ { 0x28, 0x29 }, /* '(' => ')' */ @@ -26,4 +26,11 @@ struct key_mapping il_mapping[] __keymap = { { 0x7d, 0x7b }, /* '}' => '{' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "il" keyboard map */ +struct keymap il_keymap __keymap = { + .name = "il", + .basic = il_basic, }; diff --git a/src/hci/keymap/keymap_it.c b/src/hci/keymap/keymap_it.c index 5a8e2b38d..a4921020a 100644 --- a/src/hci/keymap/keymap_it.c +++ b/src/hci/keymap/keymap_it.c @@ -10,8 +10,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "it" keyboard mapping */ -struct key_mapping it_mapping[] __keymap = { +/** "it" basic remapping */ +static struct keymap_key it_basic[] = { { 0x1e, 0x36 }, /* 0x1e => '6' */ { 0x26, 0x2f }, /* '&' => '/' */ { 0x28, 0x29 }, /* '(' => ')' */ @@ -32,4 +32,11 @@ struct key_mapping it_mapping[] __keymap = { { 0x7e, 0x7c }, /* '~' => '|' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "it" keyboard map */ +struct keymap it_keymap __keymap = { + .name = "it", + .basic = it_basic, }; diff --git a/src/hci/keymap/keymap_lt.c b/src/hci/keymap/keymap_lt.c index 3e99d8c6c..333241d21 100644 --- a/src/hci/keymap/keymap_lt.c +++ b/src/hci/keymap/keymap_lt.c @@ -10,6 +10,13 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "lt" keyboard mapping */ -struct key_mapping lt_mapping[] __keymap = { +/** "lt" basic remapping */ +static struct keymap_key lt_basic[] = { + { 0, 0 } +}; + +/** "lt" keyboard map */ +struct keymap lt_keymap __keymap = { + .name = "lt", + .basic = lt_basic, }; diff --git a/src/hci/keymap/keymap_mk.c b/src/hci/keymap/keymap_mk.c index 9f2cff78b..1656fb99c 100644 --- a/src/hci/keymap/keymap_mk.c +++ b/src/hci/keymap/keymap_mk.c @@ -10,8 +10,15 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "mk" keyboard mapping */ -struct key_mapping mk_mapping[] __keymap = { +/** "mk" basic remapping */ +static struct keymap_key mk_basic[] = { { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "mk" keyboard map */ +struct keymap mk_keymap __keymap = { + .name = "mk", + .basic = mk_basic, }; diff --git a/src/hci/keymap/keymap_mt.c b/src/hci/keymap/keymap_mt.c index dfca2ff66..ebff8506f 100644 --- a/src/hci/keymap/keymap_mt.c +++ b/src/hci/keymap/keymap_mt.c @@ -10,11 +10,18 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "mt" keyboard mapping */ -struct key_mapping mt_mapping[] __keymap = { +/** "mt" basic remapping */ +static struct keymap_key mt_basic[] = { { 0x1c, 0x1e }, /* 0x1c => 0x1e */ { 0x22, 0x40 }, /* '"' => '@' */ { 0x40, 0x22 }, /* '@' => '"' */ { 0x5c, 0x23 }, /* '\\' => '#' */ { 0x7c, 0x7e }, /* '|' => '~' */ + { 0, 0 } +}; + +/** "mt" keyboard map */ +struct keymap mt_keymap __keymap = { + .name = "mt", + .basic = mt_basic, }; diff --git a/src/hci/keymap/keymap_nl.c b/src/hci/keymap/keymap_nl.c index d248fc8ae..2172e045f 100644 --- a/src/hci/keymap/keymap_nl.c +++ b/src/hci/keymap/keymap_nl.c @@ -10,8 +10,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "nl" keyboard mapping */ -struct key_mapping nl_mapping[] __keymap = { +/** "nl" basic remapping */ +static struct keymap_key nl_basic[] = { { 0x1c, 0x3c }, /* 0x1c => '<' */ { 0x1d, 0x1c }, /* 0x1d => 0x1c */ { 0x1e, 0x36 }, /* 0x1e => '6' */ @@ -35,4 +35,11 @@ struct key_mapping nl_mapping[] __keymap = { { 0x7d, 0x7c }, /* '}' => '|' */ { 0xdc, 0x5d }, /* Pseudo-'\\' => ']' */ { 0xfc, 0x5b }, /* Pseudo-'|' => '[' */ + { 0, 0 } +}; + +/** "nl" keyboard map */ +struct keymap nl_keymap __keymap = { + .name = "nl", + .basic = nl_basic, }; diff --git a/src/hci/keymap/keymap_no-latin1.c b/src/hci/keymap/keymap_no-latin1.c index d5a721a90..65f30beae 100644 --- a/src/hci/keymap/keymap_no-latin1.c +++ b/src/hci/keymap/keymap_no-latin1.c @@ -10,8 +10,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "no-latin1" keyboard mapping */ -struct key_mapping no_latin1_mapping[] __keymap = { +/** "no-latin1" basic remapping */ +static struct keymap_key no_latin1_basic[] = { { 0x1d, 0x1e }, /* 0x1d => 0x1e */ { 0x26, 0x2f }, /* '&' => '/' */ { 0x28, 0x29 }, /* '(' => ')' */ @@ -34,4 +34,11 @@ struct key_mapping no_latin1_mapping[] __keymap = { { 0x7d, 0x5e }, /* '}' => '^' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "no-latin1" keyboard map */ +struct keymap no_latin1_keymap __keymap = { + .name = "no-latin1", + .basic = no_latin1_basic, }; diff --git a/src/hci/keymap/keymap_no.c b/src/hci/keymap/keymap_no.c index b6190da4a..d3d06bce3 100644 --- a/src/hci/keymap/keymap_no.c +++ b/src/hci/keymap/keymap_no.c @@ -10,8 +10,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "no" keyboard mapping */ -struct key_mapping no_mapping[] __keymap = { +/** "no" basic remapping */ +static struct keymap_key no_basic[] = { { 0x1c, 0x27 }, /* 0x1c => '\'' */ { 0x1e, 0x36 }, /* 0x1e => '6' */ { 0x26, 0x2f }, /* '&' => '/' */ @@ -32,4 +32,11 @@ struct key_mapping no_mapping[] __keymap = { { 0x7c, 0x2a }, /* '|' => '*' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "no" keyboard map */ +struct keymap no_keymap __keymap = { + .name = "no", + .basic = no_basic, }; diff --git a/src/hci/keymap/keymap_pl.c b/src/hci/keymap/keymap_pl.c index 224fbde28..a23c01f2c 100644 --- a/src/hci/keymap/keymap_pl.c +++ b/src/hci/keymap/keymap_pl.c @@ -10,8 +10,15 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "pl" keyboard mapping */ -struct key_mapping pl_mapping[] __keymap = { +/** "pl" basic remapping */ +static struct keymap_key pl_basic[] = { { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "pl" keyboard map */ +struct keymap pl_keymap __keymap = { + .name = "pl", + .basic = pl_basic, }; diff --git a/src/hci/keymap/keymap_pt.c b/src/hci/keymap/keymap_pt.c index 6d850fee8..c065fd76f 100644 --- a/src/hci/keymap/keymap_pt.c +++ b/src/hci/keymap/keymap_pt.c @@ -10,8 +10,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "pt" keyboard mapping */ -struct key_mapping pt_mapping[] __keymap = { +/** "pt" basic remapping */ +static struct keymap_key pt_basic[] = { { 0x1e, 0x36 }, /* 0x1e => '6' */ { 0x26, 0x2f }, /* '&' => '/' */ { 0x28, 0x29 }, /* '(' => ')' */ @@ -31,4 +31,11 @@ struct key_mapping pt_mapping[] __keymap = { { 0x7e, 0x7c }, /* '~' => '|' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "pt" keyboard map */ +struct keymap pt_keymap __keymap = { + .name = "pt", + .basic = pt_basic, }; diff --git a/src/hci/keymap/keymap_ro.c b/src/hci/keymap/keymap_ro.c index 0eef7d534..334cf6080 100644 --- a/src/hci/keymap/keymap_ro.c +++ b/src/hci/keymap/keymap_ro.c @@ -10,6 +10,13 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "ro" keyboard mapping */ -struct key_mapping ro_mapping[] __keymap = { +/** "ro" basic remapping */ +static struct keymap_key ro_basic[] = { + { 0, 0 } +}; + +/** "ro" keyboard map */ +struct keymap ro_keymap __keymap = { + .name = "ro", + .basic = ro_basic, }; diff --git a/src/hci/keymap/keymap_ru.c b/src/hci/keymap/keymap_ru.c index f7611c30a..a08b115ed 100644 --- a/src/hci/keymap/keymap_ru.c +++ b/src/hci/keymap/keymap_ru.c @@ -10,9 +10,16 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "ru" keyboard mapping */ -struct key_mapping ru_mapping[] __keymap = { +/** "ru" basic remapping */ +static struct keymap_key ru_basic[] = { { 0x0d, 0x0a }, /* Ctrl-M => Ctrl-J */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "ru" keyboard map */ +struct keymap ru_keymap __keymap = { + .name = "ru", + .basic = ru_basic, }; diff --git a/src/hci/keymap/keymap_sg.c b/src/hci/keymap/keymap_sg.c index 9a515c745..152c5d631 100644 --- a/src/hci/keymap/keymap_sg.c +++ b/src/hci/keymap/keymap_sg.c @@ -10,8 +10,8 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "sg" keyboard mapping */ -struct key_mapping sg_mapping[] __keymap = { +/** "sg" basic remapping */ +static struct keymap_key sg_basic[] = { { 0x19, 0x1a }, /* Ctrl-Y => Ctrl-Z */ { 0x1a, 0x19 }, /* Ctrl-Z => Ctrl-Y */ { 0x21, 0x2b }, /* '!' => '+' */ @@ -40,4 +40,11 @@ struct key_mapping sg_mapping[] __keymap = { { 0x7d, 0x21 }, /* '}' => '!' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "sg" keyboard map */ +struct keymap sg_keymap __keymap = { + .name = "sg", + .basic = sg_basic, }; diff --git a/src/hci/keymap/keymap_sr-latin.c b/src/hci/keymap/keymap_sr-latin.c index 1d4588733..ec5efdc89 100644 --- a/src/hci/keymap/keymap_sr-latin.c +++ b/src/hci/keymap/keymap_sr-latin.c @@ -10,8 +10,15 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "sr-latin" keyboard mapping */ -struct key_mapping sr_latin_mapping[] __keymap = { +/** "sr-latin" basic remapping */ +static struct keymap_key sr_latin_basic[] = { { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "sr-latin" keyboard map */ +struct keymap sr_latin_keymap __keymap = { + .name = "sr-latin", + .basic = sr_latin_basic, }; diff --git a/src/hci/keymap/keymap_ua.c b/src/hci/keymap/keymap_ua.c index 50f2e184d..b4199cdad 100644 --- a/src/hci/keymap/keymap_ua.c +++ b/src/hci/keymap/keymap_ua.c @@ -10,8 +10,15 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "ua" keyboard mapping */ -struct key_mapping ua_mapping[] __keymap = { +/** "ua" basic remapping */ +static struct keymap_key ua_basic[] = { { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "ua" keyboard map */ +struct keymap ua_keymap __keymap = { + .name = "ua", + .basic = ua_basic, }; diff --git a/src/hci/keymap/keymap_uk.c b/src/hci/keymap/keymap_uk.c index 6550d8ee5..156b42dff 100644 --- a/src/hci/keymap/keymap_uk.c +++ b/src/hci/keymap/keymap_uk.c @@ -10,10 +10,17 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "uk" keyboard mapping */ -struct key_mapping uk_mapping[] __keymap = { +/** "uk" basic remapping */ +static struct keymap_key uk_basic[] = { { 0x22, 0x40 }, /* '"' => '@' */ { 0x40, 0x22 }, /* '@' => '"' */ { 0x5c, 0x23 }, /* '\\' => '#' */ { 0x7c, 0x7e }, /* '|' => '~' */ + { 0, 0 } +}; + +/** "uk" keyboard map */ +struct keymap uk_keymap __keymap = { + .name = "uk", + .basic = uk_basic, }; diff --git a/src/hci/keymap/keymap_us.c b/src/hci/keymap/keymap_us.c index 73d01a30a..5d78f80a2 100644 --- a/src/hci/keymap/keymap_us.c +++ b/src/hci/keymap/keymap_us.c @@ -10,6 +10,13 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); #include -/** "us" keyboard mapping */ -struct key_mapping us_mapping[] __keymap = { +/** "us" basic remapping */ +static struct keymap_key us_basic[] = { + { 0, 0 } +}; + +/** "us" keyboard map */ +struct keymap us_keymap __keymap = { + .name = "us", + .basic = us_basic, }; diff --git a/src/include/ipxe/keymap.h b/src/include/ipxe/keymap.h index 93c9e7314..a64ab9cd4 100644 --- a/src/include/ipxe/keymap.h +++ b/src/include/ipxe/keymap.h @@ -13,16 +13,29 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include -/** A keyboard mapping */ -struct key_mapping { +/** A remapped key + * + * Represents a mapping from an ASCII character (as interpreted from a + * keyboard scancode by the US-only keyboard driver provided by the + * BIOS) to the appropriate ASCII value for the keyboard layout. + */ +struct keymap_key { /** Character read from keyboard */ uint8_t from; /** Character to be used instead */ uint8_t to; } __attribute__ (( packed )); +/** A keyboard mapping */ +struct keymap { + /** Name */ + const char *name; + /** Basic remapping table (zero-terminated) */ + struct keymap_key *basic; +}; + /** Keyboard mapping table */ -#define KEYMAP __table ( struct key_mapping, "keymap" ) +#define KEYMAP __table ( struct keymap, "keymap" ) /** Define a keyboard mapping */ #define __keymap __table_entry ( KEYMAP, 01 ) diff --git a/src/util/genkeymap.py b/src/util/genkeymap.py index 081e314cc..d38552eb4 100755 --- a/src/util/genkeymap.py +++ b/src/util/genkeymap.py @@ -79,7 +79,7 @@ class KeyModifiers(Flag): return 3 + bin(self.value).count('1') -@dataclass +@dataclass(frozen=True) class Key: """A single key definition""" @@ -120,8 +120,8 @@ class Key: return None -class KeyMapping(UserDict[KeyModifiers, Sequence[Key]]): - """A keyboard mapping""" +class KeyLayout(UserDict[KeyModifiers, Sequence[Key]]): + """A keyboard layout""" BKEYMAP_MAGIC: ClassVar[bytes] = b'bkeymap' """Magic signature for output produced by 'loadkeys -b'""" @@ -163,16 +163,16 @@ class KeyMapping(UserDict[KeyModifiers, Sequence[Key]]): @property def unshifted(self): - """Basic unshifted key mapping""" + """Basic unshifted keyboard layout""" return self[KeyModifiers.NONE] @property def shifted(self): - """Basic shifted key mapping""" + """Basic shifted keyboard layout""" return self[KeyModifiers.SHIFT] @classmethod - def load(cls, name: str) -> KeyMapping: + def load(cls, name: str) -> KeyLayout: """Load keymap using 'loadkeys -b'""" bkeymap = subprocess.check_output(["loadkeys", "-u", "-b", name]) if not bkeymap.startswith(cls.BKEYMAP_MAGIC): @@ -181,21 +181,21 @@ class KeyMapping(UserDict[KeyModifiers, Sequence[Key]]): included = bkeymap[:cls.MAX_NR_KEYMAPS] if len(included) != cls.MAX_NR_KEYMAPS: raise ValueError("Invalid bkeymap inclusion list") - keymaps = bkeymap[cls.MAX_NR_KEYMAPS:] + bkeymap = bkeymap[cls.MAX_NR_KEYMAPS:] keys = {} for modifiers in map(KeyModifiers, range(cls.MAX_NR_KEYMAPS)): if included[modifiers.value]: fmt = Struct('<%dH' % cls.NR_KEYS) - keymap = keymaps[:fmt.size] - if len(keymap) != fmt.size: + bkeylist = bkeymap[:fmt.size] + if len(bkeylist) != fmt.size: raise ValueError("Invalid bkeymap map %#x" % modifiers.value) keys[modifiers] = [ Key(modifiers=modifiers, keycode=keycode, keysym=keysym) - for keycode, keysym in enumerate(fmt.unpack(keymap)) + for keycode, keysym in enumerate(fmt.unpack(bkeylist)) ] - keymaps = keymaps[len(keymap):] - if keymaps: + bkeymap = bkeymap[len(bkeylist):] + if bkeymap: raise ValueError("Trailing bkeymap data") for modifiers, fixups in cls.FIXUPS.get(name, {}).items(): for keycode, keysym in fixups: @@ -218,8 +218,8 @@ class KeyMapping(UserDict[KeyModifiers, Sequence[Key]]): } -class BiosKeyMapping(KeyMapping): - """Keyboard mapping as used by the BIOS +class BiosKeyLayout(KeyLayout): + """Keyboard layout as used by the BIOS To allow for remappings of the somewhat interesting key 86, we arrange for our keyboard drivers to generate this key as "\\|" @@ -247,22 +247,56 @@ class BiosKeyMapping(KeyMapping): return inverse +class KeymapKeys(UserDict[str, str]): + """An ASCII character remapping""" + + @classmethod + def ascii_name(cls, char: str) -> str: + """ASCII character name""" + if char == '\\': + name = "'\\\\'" + elif char == '\'': + name = "'\\\''" + elif ord(char) & BiosKeyLayout.KEY_PSEUDO: + name = "Pseudo-%s" % cls.ascii_name( + chr(ord(char) & ~BiosKeyLayout.KEY_PSEUDO) + ) + elif char.isprintable(): + name = "'%s'" % char + elif ord(char) <= 0x1a: + name = "Ctrl-%c" % (ord(char) + 0x40) + else: + name = "0x%02x" % ord(char) + return name + + @property + def code(self): + """Generated source code for C array""" + return '{\n' + ''.join( + '\t{ 0x%02x, 0x%02x },\t/* %s => %s */\n' % ( + ord(source), ord(target), + self.ascii_name(source), self.ascii_name(target) + ) + for source, target in self.items() + ) + '\t{ 0, 0 }\n}' + + @dataclass -class KeyRemapping: - """A keyboard remapping""" +class Keymap: + """An iPXE keyboard mapping""" name: str """Mapping name""" - source: KeyMapping - """Source keyboard mapping""" + source: KeyLayout + """Source keyboard layout""" - target: KeyMapping - """Target keyboard mapping""" + target: KeyLayout + """Target keyboard layout""" @property - def ascii(self) -> MutableMapping[str, str]: - """Remapped ASCII key table""" + def basic(self) -> KeymapKeys: + """Basic remapping table""" # Construct raw mapping from source ASCII to target ASCII raw = {source: self.target[key.modifiers][key.keycode].ascii for source, key in self.source.inverse.items()} @@ -273,7 +307,7 @@ class KeyRemapping: if target and ord(source) != 0x7f and ord(target) != 0x7f - and ord(source) & ~BiosKeyMapping.KEY_PSEUDO != ord(target)} + and ord(source) & ~BiosKeyLayout.KEY_PSEUDO != ord(target)} # Recursively delete any mappings that would produce # unreachable alphanumerics (e.g. the "il" keymap, which maps # away the whole lower-case alphabet) @@ -291,35 +325,17 @@ class KeyRemapping: if digits not in (shifted, unshifted): raise ValueError("Inconsistent numeric remapping %s / %s" % (unshifted, shifted)) - return dict(sorted(table.items())) + return KeymapKeys(dict(sorted(table.items()))) - @property - def cname(self) -> str: + def cname(self, suffix: str) -> str: """C variable name""" - return re.sub(r'\W', '_', self.name) + "_mapping" - - @classmethod - def ascii_name(cls, char: str) -> str: - """ASCII character name""" - if char == '\\': - name = "'\\\\'" - elif char == '\'': - name = "'\\\''" - elif ord(char) & BiosKeyMapping.KEY_PSEUDO: - name = "Pseudo-%s" % cls.ascii_name( - chr(ord(char) & ~BiosKeyMapping.KEY_PSEUDO) - ) - elif char.isprintable(): - name = "'%s'" % char - elif ord(char) <= 0x1a: - name = "Ctrl-%c" % (ord(char) + 0x40) - else: - name = "0x%02x" % ord(char) - return name + return re.sub(r'\W', '_', (self.name + '_' + suffix)) @property def code(self) -> str: """Generated source code""" + keymap_name = self.cname("keymap") + basic_name = self.cname("basic") code = textwrap.dedent(f""" /** @file * @@ -333,17 +349,15 @@ class KeyRemapping: #include - /** "{self.name}" keyboard mapping */ - struct key_mapping {self.cname}[] __keymap = {{ - """).lstrip() + ''.join( - '\t{ 0x%02x, 0x%02x },\t/* %s => %s */\n' % ( - ord(source), ord(target), - self.ascii_name(source), self.ascii_name(target) - ) - for source, target in self.ascii.items() - ) + textwrap.dedent(""" - }; - """).strip() + /** "{self.name}" basic remapping */ + static struct keymap_key {basic_name}[] = %s; + + /** "{self.name}" keyboard map */ + struct keymap {keymap_name} __keymap = {{ + \t.name = "{self.name}", + \t.basic = {basic_name}, + }}; + """).strip() % self.basic.code return code @@ -356,12 +370,12 @@ if __name__ == '__main__': parser.add_argument('layout', help="Target keyboard layout") args = parser.parse_args() - # Load source and target keymaps - source = BiosKeyMapping.load('us') - target = KeyMapping.load(args.layout) + # Load source and target keyboard layouts + source = BiosKeyLayout.load('us') + target = KeyLayout.load(args.layout) - # Construct remapping - remap = KeyRemapping(name=args.layout, source=source, target=target) + # Construct keyboard mapping + keymap = Keymap(name=args.layout, source=source, target=target) # Output generated code - print(remap.code) + print(keymap.code) -- cgit v1.2.3-55-g7522 From f2a59d5973da2041f93264609698b9b3f4ec101b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 14 Feb 2022 16:31:08 +0000 Subject: [console] Centralise handling of key modifiers Handle Ctrl and CapsLock key modifiers within key_remap(), to provide consistent behaviour across different console types. Signed-off-by: Michael Brown --- src/arch/x86/include/bios.h | 3 ++ src/arch/x86/interface/pcbios/bios_console.c | 23 +++++++++++---- src/core/keymap.c | 41 ++++++++++++++++++++++---- src/drivers/usb/usbkbd.c | 21 ++++++------- src/include/ipxe/keymap.h | 21 +++++++++++++ src/interface/efi/efi_console.c | 44 ++++++++++++++++++---------- 6 files changed, 116 insertions(+), 37 deletions(-) diff --git a/src/arch/x86/include/bios.h b/src/arch/x86/include/bios.h index 14e7acbc7..3ba8264ec 100644 --- a/src/arch/x86/include/bios.h +++ b/src/arch/x86/include/bios.h @@ -6,6 +6,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define BDA_SEG 0x0040 #define BDA_EBDA 0x000e #define BDA_EQUIPMENT_WORD 0x0010 +#define BDA_KB0 0x0017 +#define BDA_KB0_CTRL 0x04 +#define BDA_KB0_CAPSLOCK 0x040 #define BDA_FBMS 0x0013 #define BDA_TICKS 0x006c #define BDA_MIDNIGHT 0x0070 diff --git a/src/arch/x86/interface/pcbios/bios_console.c b/src/arch/x86/interface/pcbios/bios_console.c index 438a01d07..2664ac8a5 100644 --- a/src/arch/x86/interface/pcbios/bios_console.c +++ b/src/arch/x86/interface/pcbios/bios_console.c @@ -361,6 +361,7 @@ static const char * bios_ansi_seq ( unsigned int scancode ) { */ static int bios_getchar ( void ) { uint16_t keypress; + uint8_t kb0; unsigned int scancode; unsigned int character; const char *ansi_seq; @@ -385,16 +386,28 @@ static int bios_getchar ( void ) { bios_inject_lock--; scancode = ( keypress >> 8 ); character = ( keypress & 0xff ); + get_real ( kb0, BDA_SEG, BDA_KB0 ); /* If it's a normal character, map (if applicable) and return it */ if ( character && ( character < 0x80 ) ) { - if ( scancode < SCANCODE_RSHIFT ) { - return key_remap ( character ); - } else if ( scancode == SCANCODE_NON_US ) { - return key_remap ( character | KEYMAP_PSEUDO ); - } else { + + /* Handle special scancodes */ + if ( scancode == SCANCODE_NON_US ) { + /* Treat as "\|" with high bit set */ + character |= KEYMAP_PSEUDO; + } else if ( scancode >= SCANCODE_RSHIFT ) { + /* Non-remappable scancode (e.g. numeric keypad) */ return character; } + + /* Apply modifiers */ + if ( kb0 & BDA_KB0_CTRL ) + character |= KEYMAP_CTRL; + if ( kb0 & BDA_KB0_CAPSLOCK ) + character |= KEYMAP_CAPSLOCK_REDO; + + /* Map and return */ + return key_remap ( character ); } /* Otherwise, check for a special key that we know about */ diff --git a/src/core/keymap.c b/src/core/keymap.c index c0953967a..a5209bc20 100644 --- a/src/core/keymap.c +++ b/src/core/keymap.c @@ -23,6 +23,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include +#include #include /** @file @@ -31,6 +33,18 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +/** ASCII character mask */ +#define ASCII_MASK 0x7f + +/** Control character mask */ +#define CTRL_MASK 0x1f + +/** Upper case character mask */ +#define UPPER_MASK 0x5f + +/** Case toggle bit */ +#define CASE_TOGGLE ( ASCII_MASK & ~UPPER_MASK ) + /** Default keyboard mapping */ static TABLE_START ( keymap_start, KEYMAP ); @@ -41,21 +55,36 @@ static struct keymap *keymap = keymap_start; * Remap a key * * @v character Character read from console - * @ret character Mapped character + * @ret mapped Mapped character */ unsigned int key_remap ( unsigned int character ) { + unsigned int mapped = ( character & KEYMAP_MASK ); struct keymap_key *key; + /* Invert case before remapping if applicable */ + if ( ( character & KEYMAP_CAPSLOCK_UNDO ) && isalpha ( mapped ) ) + mapped ^= CASE_TOGGLE; + /* Remap via table */ for ( key = keymap->basic ; key->from ; key++ ) { - if ( key->from == character ) { - character = key->to; + if ( mapped == key->from ) { + mapped = key->to; break; } } - /* Clear pseudo key flag */ - character &= ~KEYMAP_PSEUDO; + /* Handle Ctrl- and CapsLock */ + if ( isalpha ( mapped ) ) { + if ( character & KEYMAP_CTRL ) { + mapped &= CTRL_MASK; + } else if ( character & KEYMAP_CAPSLOCK ) { + mapped ^= CASE_TOGGLE; + } + } + + /* Clear flags */ + mapped &= ASCII_MASK; - return character; + DBGC2 ( &keymap, "KEYMAP mapped %04x => %02x\n", character, mapped ); + return mapped; } diff --git a/src/drivers/usb/usbkbd.c b/src/drivers/usb/usbkbd.c index 6954cd69b..516667b25 100644 --- a/src/drivers/usb/usbkbd.c +++ b/src/drivers/usb/usbkbd.c @@ -71,6 +71,9 @@ static unsigned int usbkbd_map ( unsigned int keycode, unsigned int modifiers, } else if ( keycode <= USBKBD_KEY_Z ) { /* Alphabetic keys */ key = ( keycode - USBKBD_KEY_A + 'a' ); + if ( modifiers & USBKBD_SHIFT ) { + key -= ( 'a' - 'A' ); + } } else if ( keycode <= USBKBD_KEY_0 ) { /* Numeric key row */ if ( modifiers & USBKBD_SHIFT ) { @@ -125,17 +128,15 @@ static unsigned int usbkbd_map ( unsigned int keycode, unsigned int modifiers, /* Remap key if applicable */ if ( ( keycode < USBKBD_KEY_CAPS_LOCK ) || ( keycode == USBKBD_KEY_NON_US ) ) { - key = key_remap ( key ); - } - /* Handle upper/lower case and Ctrl- */ - if ( islower ( key ) ) { - if ( modifiers & USBKBD_CTRL ) { - key -= ( 'a' - CTRL_A ); - } else if ( ( modifiers & USBKBD_SHIFT ) || - ( leds & USBKBD_LED_CAPS_LOCK ) ) { - key -= ( 'a' - 'A' ); - } + /* Apply modifiers */ + if ( modifiers & USBKBD_CTRL ) + key |= KEYMAP_CTRL; + if ( leds & USBKBD_LED_CAPS_LOCK ) + key |= KEYMAP_CAPSLOCK; + + /* Remap key */ + key = key_remap ( key ); } return key; diff --git a/src/include/ipxe/keymap.h b/src/include/ipxe/keymap.h index a64ab9cd4..3da25190b 100644 --- a/src/include/ipxe/keymap.h +++ b/src/include/ipxe/keymap.h @@ -40,9 +40,30 @@ struct keymap { /** Define a keyboard mapping */ #define __keymap __table_entry ( KEYMAP, 01 ) +/** Mappable character mask */ +#define KEYMAP_MASK 0xff + /** Pseudo key flag */ #define KEYMAP_PSEUDO 0x80 +/** Ctrl key flag */ +#define KEYMAP_CTRL 0x0100 + +/** CapsLock key flag */ +#define KEYMAP_CAPSLOCK 0x0200 + +/** Undo CapsLock key flag + * + * Used when the keyboard driver has already interpreted the CapsLock + * key, in which case the effect needs to be undone before remapping + * in order to correctly handle keyboard mappings that swap alphabetic + * and non-alphabetic keys. + */ +#define KEYMAP_CAPSLOCK_UNDO 0x0400 + +/** Undo and redo CapsLock key flags */ +#define KEYMAP_CAPSLOCK_REDO ( KEYMAP_CAPSLOCK | KEYMAP_CAPSLOCK_UNDO ) + extern unsigned int key_remap ( unsigned int character ); #endif /* _IPXE_KEYMAP_H */ diff --git a/src/interface/efi/efi_console.c b/src/interface/efi/efi_console.c index 874f54b6c..9adce4a9b 100644 --- a/src/interface/efi/efi_console.c +++ b/src/interface/efi/efi_console.c @@ -55,8 +55,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ATTR_DEFAULT ATTR_FCOL_WHITE -#define CTRL_MASK 0x1f - /* Set default console usage if applicable */ #if ! ( defined ( CONSOLE_EFI ) && CONSOLE_EXPLICIT ( CONSOLE_EFI ) ) #undef CONSOLE_EFI @@ -286,6 +284,9 @@ static int efi_getchar ( void ) { EFI_SIMPLE_TEXT_INPUT_PROTOCOL *conin = efi_systab->ConIn; EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *conin_ex = efi_conin_ex; const char *ansi_seq; + unsigned int character; + unsigned int shift; + unsigned int toggle; EFI_KEY_DATA key; EFI_STATUS efirc; int rc; @@ -318,23 +319,34 @@ static int efi_getchar ( void ) { key.KeyState.KeyToggleState, key.Key.UnicodeChar, key.Key.ScanCode ); - /* Remap key. There is unfortunately no way to avoid - * remapping the numeric keypad, since EFI destroys the scan - * code information that would allow us to differentiate - * between main keyboard and numeric keypad. + /* If key has a Unicode representation, remap and return it. + * There is unfortunately no way to avoid remapping the + * numeric keypad, since EFI destroys the scan code + * information that would allow us to differentiate between + * main keyboard and numeric keypad. */ - key.Key.UnicodeChar = key_remap ( key.Key.UnicodeChar ); + if ( ( character = key.Key.UnicodeChar ) != 0 ) { + + /* Apply shift state */ + shift = key.KeyState.KeyShiftState; + if ( shift & EFI_SHIFT_STATE_VALID ) { + if ( shift & ( EFI_LEFT_CONTROL_PRESSED | + EFI_RIGHT_CONTROL_PRESSED ) ) { + character |= KEYMAP_CTRL; + } + } - /* Translate Ctrl- */ - if ( ( key.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID ) && - ( key.KeyState.KeyShiftState & ( EFI_LEFT_CONTROL_PRESSED | - EFI_RIGHT_CONTROL_PRESSED ) ) ) { - key.Key.UnicodeChar &= CTRL_MASK; - } + /* Apply toggle state */ + toggle = key.KeyState.KeyToggleState; + if ( toggle & EFI_TOGGLE_STATE_VALID ) { + if ( toggle & EFI_CAPS_LOCK_ACTIVE ) { + character |= KEYMAP_CAPSLOCK_REDO; + } + } - /* If key has a Unicode representation, return it */ - if ( key.Key.UnicodeChar ) - return key.Key.UnicodeChar; + /* Remap and return key */ + return key_remap ( character ); + } /* Otherwise, check for a special key that we know about */ if ( ( ansi_seq = scancode_to_ansi_seq ( key.Key.ScanCode ) ) ) { -- cgit v1.2.3-55-g7522 From e1cedbc0d4fdb0e16818f6b722f4873a50780761 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 14 Feb 2022 13:45:59 +0000 Subject: [console] Support AltGr to access ASCII characters via remapping Several keyboard layouts define ASCII characters as accessible only via the AltGr modifier. Add support for this modifier to ensure that all ASCII characters are accessible. Experiments suggest that the BIOS console is likely to fail to generate ASCII characters when the AltGr key is pressed. Work around this limitation by accepting LShift+RShift (which will definitely produce an ASCII character) as a synonym for AltGr. Signed-off-by: Michael Brown --- src/arch/x86/include/bios.h | 4 +++ src/arch/x86/interface/pcbios/bios_console.c | 12 +++++++++ src/core/keymap.c | 5 +++- src/drivers/usb/usbkbd.c | 2 ++ src/hci/keymap/keymap_al.c | 8 ++++++ src/hci/keymap/keymap_az.c | 7 +++++ src/hci/keymap/keymap_by.c | 6 +++++ src/hci/keymap/keymap_cf.c | 9 +++++++ src/hci/keymap/keymap_cz.c | 26 ++++++++++++++++++ src/hci/keymap/keymap_de.c | 11 ++++++++ src/hci/keymap/keymap_dk.c | 9 +++++++ src/hci/keymap/keymap_es.c | 11 ++++++++ src/hci/keymap/keymap_et.c | 10 +++++++ src/hci/keymap/keymap_fi.c | 9 +++++++ src/hci/keymap/keymap_fr.c | 12 +++++++++ src/hci/keymap/keymap_gr.c | 6 +++++ src/hci/keymap/keymap_hu.c | 17 ++++++++++++ src/hci/keymap/keymap_il.c | 6 +++++ src/hci/keymap/keymap_it.c | 12 +++++++++ src/hci/keymap/keymap_lt.c | 6 +++++ src/hci/keymap/keymap_mk.c | 6 +++++ src/hci/keymap/keymap_mt.c | 7 +++++ src/hci/keymap/keymap_nl.c | 8 ++++++ src/hci/keymap/keymap_no-latin1.c | 10 +++++++ src/hci/keymap/keymap_no.c | 8 ++++++ src/hci/keymap/keymap_pl.c | 6 +++++ src/hci/keymap/keymap_pt.c | 10 +++++++ src/hci/keymap/keymap_ro.c | 6 +++++ src/hci/keymap/keymap_ru.c | 6 +++++ src/hci/keymap/keymap_sg.c | 10 +++++++ src/hci/keymap/keymap_sr-latin.c | 6 +++++ src/hci/keymap/keymap_ua.c | 6 +++++ src/hci/keymap/keymap_uk.c | 6 +++++ src/hci/keymap/keymap_us.c | 6 +++++ src/include/ipxe/keymap.h | 5 ++++ src/interface/efi/efi_console.c | 3 +++ src/util/genkeymap.py | 40 +++++++++++++++++++++++++--- 37 files changed, 332 insertions(+), 5 deletions(-) diff --git a/src/arch/x86/include/bios.h b/src/arch/x86/include/bios.h index 3ba8264ec..6391a4958 100644 --- a/src/arch/x86/include/bios.h +++ b/src/arch/x86/include/bios.h @@ -7,6 +7,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define BDA_EBDA 0x000e #define BDA_EQUIPMENT_WORD 0x0010 #define BDA_KB0 0x0017 +#define BDA_KB0_RSHIFT 0x01 +#define BDA_KB0_LSHIFT 0x02 #define BDA_KB0_CTRL 0x04 #define BDA_KB0_CAPSLOCK 0x040 #define BDA_FBMS 0x0013 @@ -16,5 +18,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define BDA_REBOOT_WARM 0x1234 #define BDA_NUM_DRIVES 0x0075 #define BDA_CHAR_HEIGHT 0x0085 +#define BDA_KB2 0x0096 +#define BDA_KB2_RALT 0x08 #endif /* BIOS_H */ diff --git a/src/arch/x86/interface/pcbios/bios_console.c b/src/arch/x86/interface/pcbios/bios_console.c index 2664ac8a5..0220c8564 100644 --- a/src/arch/x86/interface/pcbios/bios_console.c +++ b/src/arch/x86/interface/pcbios/bios_console.c @@ -362,6 +362,7 @@ static const char * bios_ansi_seq ( unsigned int scancode ) { static int bios_getchar ( void ) { uint16_t keypress; uint8_t kb0; + uint8_t kb2; unsigned int scancode; unsigned int character; const char *ansi_seq; @@ -387,6 +388,7 @@ static int bios_getchar ( void ) { scancode = ( keypress >> 8 ); character = ( keypress & 0xff ); get_real ( kb0, BDA_SEG, BDA_KB0 ); + get_real ( kb2, BDA_SEG, BDA_KB2 ); /* If it's a normal character, map (if applicable) and return it */ if ( character && ( character < 0x80 ) ) { @@ -405,6 +407,16 @@ static int bios_getchar ( void ) { character |= KEYMAP_CTRL; if ( kb0 & BDA_KB0_CAPSLOCK ) character |= KEYMAP_CAPSLOCK_REDO; + if ( kb2 & BDA_KB2_RALT ) + character |= KEYMAP_ALTGR; + + /* Treat LShift+RShift as AltGr since many BIOSes will + * not return ASCII characters when AltGr is pressed. + */ + if ( ( kb0 & ( BDA_KB0_LSHIFT | BDA_KB0_RSHIFT ) ) == + ( BDA_KB0_LSHIFT | BDA_KB0_RSHIFT ) ) { + character |= KEYMAP_ALTGR; + } /* Map and return */ return key_remap ( character ); diff --git a/src/core/keymap.c b/src/core/keymap.c index a5209bc20..3fa85f74e 100644 --- a/src/core/keymap.c +++ b/src/core/keymap.c @@ -65,8 +65,11 @@ unsigned int key_remap ( unsigned int character ) { if ( ( character & KEYMAP_CAPSLOCK_UNDO ) && isalpha ( mapped ) ) mapped ^= CASE_TOGGLE; + /* Select remapping table */ + key = ( ( character & KEYMAP_ALTGR ) ? keymap->altgr : keymap->basic ); + /* Remap via table */ - for ( key = keymap->basic ; key->from ; key++ ) { + for ( ; key->from ; key++ ) { if ( mapped == key->from ) { mapped = key->to; break; diff --git a/src/drivers/usb/usbkbd.c b/src/drivers/usb/usbkbd.c index 516667b25..b284e584f 100644 --- a/src/drivers/usb/usbkbd.c +++ b/src/drivers/usb/usbkbd.c @@ -132,6 +132,8 @@ static unsigned int usbkbd_map ( unsigned int keycode, unsigned int modifiers, /* Apply modifiers */ if ( modifiers & USBKBD_CTRL ) key |= KEYMAP_CTRL; + if ( modifiers & USBKBD_ALT_RIGHT ) + key |= KEYMAP_ALTGR; if ( leds & USBKBD_LED_CAPS_LOCK ) key |= KEYMAP_CAPSLOCK; diff --git a/src/hci/keymap/keymap_al.c b/src/hci/keymap/keymap_al.c index a3df385a9..b68b98878 100644 --- a/src/hci/keymap/keymap_al.c +++ b/src/hci/keymap/keymap_al.c @@ -35,8 +35,16 @@ static struct keymap_key al_basic[] = { { 0, 0 } }; +/** "al" AltGr remapping */ +static struct keymap_key al_altgr[] = { + { 0x31, 0x7e }, /* '1' => '~' */ + { 0x37, 0x60 }, /* '7' => '`' */ + { 0, 0 } +}; + /** "al" keyboard map */ struct keymap al_keymap __keymap = { .name = "al", .basic = al_basic, + .altgr = al_altgr, }; diff --git a/src/hci/keymap/keymap_az.c b/src/hci/keymap/keymap_az.c index 7b382ca8b..03087e01e 100644 --- a/src/hci/keymap/keymap_az.c +++ b/src/hci/keymap/keymap_az.c @@ -26,8 +26,15 @@ static struct keymap_key az_basic[] = { { 0, 0 } }; +/** "az" AltGr remapping */ +static struct keymap_key az_altgr[] = { + { 0xdc, 0x7c }, /* Pseudo-'\\' => '|' */ + { 0, 0 } +}; + /** "az" keyboard map */ struct keymap az_keymap __keymap = { .name = "az", .basic = az_basic, + .altgr = az_altgr, }; diff --git a/src/hci/keymap/keymap_by.c b/src/hci/keymap/keymap_by.c index 4127609e3..9af6c966d 100644 --- a/src/hci/keymap/keymap_by.c +++ b/src/hci/keymap/keymap_by.c @@ -17,8 +17,14 @@ static struct keymap_key by_basic[] = { { 0, 0 } }; +/** "by" AltGr remapping */ +static struct keymap_key by_altgr[] = { + { 0, 0 } +}; + /** "by" keyboard map */ struct keymap by_keymap __keymap = { .name = "by", .basic = by_basic, + .altgr = by_altgr, }; diff --git a/src/hci/keymap/keymap_cf.c b/src/hci/keymap/keymap_cf.c index 0bbe89659..09242ee6f 100644 --- a/src/hci/keymap/keymap_cf.c +++ b/src/hci/keymap/keymap_cf.c @@ -24,8 +24,17 @@ static struct keymap_key cf_basic[] = { { 0, 0 } }; +/** "cf" AltGr remapping */ +static struct keymap_key cf_altgr[] = { + { 0x32, 0x40 }, /* '2' => '@' */ + { 0x3b, 0x7e }, /* ';' => '~' */ + { 0x60, 0x5c }, /* '`' => '\\' */ + { 0, 0 } +}; + /** "cf" keyboard map */ struct keymap cf_keymap __keymap = { .name = "cf", .basic = cf_basic, + .altgr = cf_altgr, }; diff --git a/src/hci/keymap/keymap_cz.c b/src/hci/keymap/keymap_cz.c index 8655d5b68..cce686d9a 100644 --- a/src/hci/keymap/keymap_cz.c +++ b/src/hci/keymap/keymap_cz.c @@ -46,8 +46,34 @@ static struct keymap_key cz_basic[] = { { 0, 0 } }; +/** "cz" AltGr remapping */ +static struct keymap_key cz_altgr[] = { + { 0x2c, 0x3c }, /* ',' => '<' */ + { 0x2e, 0x3e }, /* '.' => '>' */ + { 0x2f, 0x2a }, /* '/' => '*' */ + { 0x30, 0x7d }, /* '0' => '}' */ + { 0x32, 0x40 }, /* '2' => '@' */ + { 0x33, 0x23 }, /* '3' => '#' */ + { 0x34, 0x24 }, /* '4' => '$' */ + { 0x36, 0x5e }, /* '6' => '^' */ + { 0x37, 0x26 }, /* '7' => '&' */ + { 0x38, 0x2a }, /* '8' => '*' */ + { 0x39, 0x7b }, /* '9' => '{' */ + { 0x3b, 0x24 }, /* ';' => '$' */ + { 0x62, 0x7b }, /* 'b' => '{' */ + { 0x63, 0x26 }, /* 'c' => '&' */ + { 0x67, 0x5d }, /* 'g' => ']' */ + { 0x68, 0x60 }, /* 'h' => '`' */ + { 0x6d, 0x5e }, /* 'm' => '^' */ + { 0x6e, 0x7d }, /* 'n' => '}' */ + { 0x76, 0x40 }, /* 'v' => '@' */ + { 0x78, 0x23 }, /* 'x' => '#' */ + { 0, 0 } +}; + /** "cz" keyboard map */ struct keymap cz_keymap __keymap = { .name = "cz", .basic = cz_basic, + .altgr = cz_altgr, }; diff --git a/src/hci/keymap/keymap_de.c b/src/hci/keymap/keymap_de.c index 4d23c2e60..4a889a242 100644 --- a/src/hci/keymap/keymap_de.c +++ b/src/hci/keymap/keymap_de.c @@ -41,8 +41,19 @@ static struct keymap_key de_basic[] = { { 0, 0 } }; +/** "de" AltGr remapping */ +static struct keymap_key de_altgr[] = { + { 0x2d, 0x5c }, /* '-' => '\\' */ + { 0x30, 0x7d }, /* '0' => '}' */ + { 0x39, 0x5d }, /* '9' => ']' */ + { 0x71, 0x40 }, /* 'q' => '@' */ + { 0xdc, 0x7c }, /* Pseudo-'\\' => '|' */ + { 0, 0 } +}; + /** "de" keyboard map */ struct keymap de_keymap __keymap = { .name = "de", .basic = de_basic, + .altgr = de_altgr, }; diff --git a/src/hci/keymap/keymap_dk.c b/src/hci/keymap/keymap_dk.c index 100246bf5..4d40743b1 100644 --- a/src/hci/keymap/keymap_dk.c +++ b/src/hci/keymap/keymap_dk.c @@ -33,8 +33,17 @@ static struct keymap_key dk_basic[] = { { 0, 0 } }; +/** "dk" AltGr remapping */ +static struct keymap_key dk_altgr[] = { + { 0x32, 0x40 }, /* '2' => '@' */ + { 0x3d, 0x7c }, /* '=' => '|' */ + { 0x71, 0x40 }, /* 'q' => '@' */ + { 0, 0 } +}; + /** "dk" keyboard map */ struct keymap dk_keymap __keymap = { .name = "dk", .basic = dk_basic, + .altgr = dk_altgr, }; diff --git a/src/hci/keymap/keymap_es.c b/src/hci/keymap/keymap_es.c index 2f4b86c47..397e2cbaa 100644 --- a/src/hci/keymap/keymap_es.c +++ b/src/hci/keymap/keymap_es.c @@ -33,8 +33,19 @@ static struct keymap_key es_basic[] = { { 0, 0 } }; +/** "es" AltGr remapping */ +static struct keymap_key es_altgr[] = { + { 0x30, 0x7d }, /* '0' => '}' */ + { 0x32, 0x40 }, /* '2' => '@' */ + { 0x39, 0x5d }, /* '9' => ']' */ + { 0x5c, 0x7d }, /* '\\' => '}' */ + { 0x71, 0x40 }, /* 'q' => '@' */ + { 0, 0 } +}; + /** "es" keyboard map */ struct keymap es_keymap __keymap = { .name = "es", .basic = es_basic, + .altgr = es_altgr, }; diff --git a/src/hci/keymap/keymap_et.c b/src/hci/keymap/keymap_et.c index a8bf46ebc..4120dbed9 100644 --- a/src/hci/keymap/keymap_et.c +++ b/src/hci/keymap/keymap_et.c @@ -31,8 +31,18 @@ static struct keymap_key et_basic[] = { { 0, 0 } }; +/** "et" AltGr remapping */ +static struct keymap_key et_altgr[] = { + { 0x27, 0x5e }, /* '\'' => '^' */ + { 0x2d, 0x5c }, /* '-' => '\\' */ + { 0x32, 0x40 }, /* '2' => '@' */ + { 0xdc, 0x7c }, /* Pseudo-'\\' => '|' */ + { 0, 0 } +}; + /** "et" keyboard map */ struct keymap et_keymap __keymap = { .name = "et", .basic = et_basic, + .altgr = et_altgr, }; diff --git a/src/hci/keymap/keymap_fi.c b/src/hci/keymap/keymap_fi.c index eb75eb4dc..978121a88 100644 --- a/src/hci/keymap/keymap_fi.c +++ b/src/hci/keymap/keymap_fi.c @@ -31,8 +31,17 @@ static struct keymap_key fi_basic[] = { { 0, 0 } }; +/** "fi" AltGr remapping */ +static struct keymap_key fi_altgr[] = { + { 0x2d, 0x5c }, /* '-' => '\\' */ + { 0x32, 0x40 }, /* '2' => '@' */ + { 0xdc, 0x7c }, /* Pseudo-'\\' => '|' */ + { 0, 0 } +}; + /** "fi" keyboard map */ struct keymap fi_keymap __keymap = { .name = "fi", .basic = fi_basic, + .altgr = fi_altgr, }; diff --git a/src/hci/keymap/keymap_fr.c b/src/hci/keymap/keymap_fr.c index 523254ee5..c0a959f0d 100644 --- a/src/hci/keymap/keymap_fr.c +++ b/src/hci/keymap/keymap_fr.c @@ -62,8 +62,20 @@ static struct keymap_key fr_basic[] = { { 0, 0 } }; +/** "fr" AltGr remapping */ +static struct keymap_key fr_altgr[] = { + { 0x2d, 0x5d }, /* '-' => ']' */ + { 0x30, 0x40 }, /* '0' => '@' */ + { 0x33, 0x23 }, /* '3' => '#' */ + { 0x38, 0x5c }, /* '8' => '\\' */ + { 0x39, 0x5e }, /* '9' => '^' */ + { 0x61, 0x40 }, /* 'a' => '@' */ + { 0, 0 } +}; + /** "fr" keyboard map */ struct keymap fr_keymap __keymap = { .name = "fr", .basic = fr_basic, + .altgr = fr_altgr, }; diff --git a/src/hci/keymap/keymap_gr.c b/src/hci/keymap/keymap_gr.c index 16a2a7032..4826c26c2 100644 --- a/src/hci/keymap/keymap_gr.c +++ b/src/hci/keymap/keymap_gr.c @@ -17,8 +17,14 @@ static struct keymap_key gr_basic[] = { { 0, 0 } }; +/** "gr" AltGr remapping */ +static struct keymap_key gr_altgr[] = { + { 0, 0 } +}; + /** "gr" keyboard map */ struct keymap gr_keymap __keymap = { .name = "gr", .basic = gr_basic, + .altgr = gr_altgr, }; diff --git a/src/hci/keymap/keymap_hu.c b/src/hci/keymap/keymap_hu.c index 5e407161f..64e27dda6 100644 --- a/src/hci/keymap/keymap_hu.c +++ b/src/hci/keymap/keymap_hu.c @@ -35,8 +35,25 @@ static struct keymap_key hu_basic[] = { { 0, 0 } }; +/** "hu" AltGr remapping */ +static struct keymap_key hu_altgr[] = { + { 0x2e, 0x3e }, /* '.' => '>' */ + { 0x2f, 0x2a }, /* '/' => '*' */ + { 0x33, 0x5e }, /* '3' => '^' */ + { 0x37, 0x60 }, /* '7' => '`' */ + { 0x3b, 0x24 }, /* ';' => '$' */ + { 0x63, 0x26 }, /* 'c' => '&' */ + { 0x6d, 0x3c }, /* 'm' => '<' */ + { 0x76, 0x40 }, /* 'v' => '@' */ + { 0x78, 0x23 }, /* 'x' => '#' */ + { 0x7a, 0x3e }, /* 'z' => '>' */ + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0, 0 } +}; + /** "hu" keyboard map */ struct keymap hu_keymap __keymap = { .name = "hu", .basic = hu_basic, + .altgr = hu_altgr, }; diff --git a/src/hci/keymap/keymap_il.c b/src/hci/keymap/keymap_il.c index de5e639ca..e3061fa54 100644 --- a/src/hci/keymap/keymap_il.c +++ b/src/hci/keymap/keymap_il.c @@ -29,8 +29,14 @@ static struct keymap_key il_basic[] = { { 0, 0 } }; +/** "il" AltGr remapping */ +static struct keymap_key il_altgr[] = { + { 0, 0 } +}; + /** "il" keyboard map */ struct keymap il_keymap __keymap = { .name = "il", .basic = il_basic, + .altgr = il_altgr, }; diff --git a/src/hci/keymap/keymap_it.c b/src/hci/keymap/keymap_it.c index a4921020a..f67bbadcb 100644 --- a/src/hci/keymap/keymap_it.c +++ b/src/hci/keymap/keymap_it.c @@ -35,8 +35,20 @@ static struct keymap_key it_basic[] = { { 0, 0 } }; +/** "it" AltGr remapping */ +static struct keymap_key it_altgr[] = { + { 0x2d, 0x60 }, /* '-' => '`' */ + { 0x30, 0x7d }, /* '0' => '}' */ + { 0x39, 0x5d }, /* '9' => ']' */ + { 0x3b, 0x40 }, /* ';' => '@' */ + { 0x3d, 0x7e }, /* '=' => '~' */ + { 0x71, 0x40 }, /* 'q' => '@' */ + { 0, 0 } +}; + /** "it" keyboard map */ struct keymap it_keymap __keymap = { .name = "it", .basic = it_basic, + .altgr = it_altgr, }; diff --git a/src/hci/keymap/keymap_lt.c b/src/hci/keymap/keymap_lt.c index 333241d21..5d6ee5a8c 100644 --- a/src/hci/keymap/keymap_lt.c +++ b/src/hci/keymap/keymap_lt.c @@ -15,8 +15,14 @@ static struct keymap_key lt_basic[] = { { 0, 0 } }; +/** "lt" AltGr remapping */ +static struct keymap_key lt_altgr[] = { + { 0, 0 } +}; + /** "lt" keyboard map */ struct keymap lt_keymap __keymap = { .name = "lt", .basic = lt_basic, + .altgr = lt_altgr, }; diff --git a/src/hci/keymap/keymap_mk.c b/src/hci/keymap/keymap_mk.c index 1656fb99c..4b90ef799 100644 --- a/src/hci/keymap/keymap_mk.c +++ b/src/hci/keymap/keymap_mk.c @@ -17,8 +17,14 @@ static struct keymap_key mk_basic[] = { { 0, 0 } }; +/** "mk" AltGr remapping */ +static struct keymap_key mk_altgr[] = { + { 0, 0 } +}; + /** "mk" keyboard map */ struct keymap mk_keymap __keymap = { .name = "mk", .basic = mk_basic, + .altgr = mk_altgr, }; diff --git a/src/hci/keymap/keymap_mt.c b/src/hci/keymap/keymap_mt.c index ebff8506f..f5baf6907 100644 --- a/src/hci/keymap/keymap_mt.c +++ b/src/hci/keymap/keymap_mt.c @@ -20,8 +20,15 @@ static struct keymap_key mt_basic[] = { { 0, 0 } }; +/** "mt" AltGr remapping */ +static struct keymap_key mt_altgr[] = { + { 0x2d, 0x5c }, /* '-' => '\\' */ + { 0, 0 } +}; + /** "mt" keyboard map */ struct keymap mt_keymap __keymap = { .name = "mt", .basic = mt_basic, + .altgr = mt_altgr, }; diff --git a/src/hci/keymap/keymap_nl.c b/src/hci/keymap/keymap_nl.c index 2172e045f..bbee4cbdf 100644 --- a/src/hci/keymap/keymap_nl.c +++ b/src/hci/keymap/keymap_nl.c @@ -38,8 +38,16 @@ static struct keymap_key nl_basic[] = { { 0, 0 } }; +/** "nl" AltGr remapping */ +static struct keymap_key nl_altgr[] = { + { 0x2d, 0x5c }, /* '-' => '\\' */ + { 0x39, 0x7d }, /* '9' => '}' */ + { 0, 0 } +}; + /** "nl" keyboard map */ struct keymap nl_keymap __keymap = { .name = "nl", .basic = nl_basic, + .altgr = nl_altgr, }; diff --git a/src/hci/keymap/keymap_no-latin1.c b/src/hci/keymap/keymap_no-latin1.c index 65f30beae..63fe85548 100644 --- a/src/hci/keymap/keymap_no-latin1.c +++ b/src/hci/keymap/keymap_no-latin1.c @@ -37,8 +37,18 @@ static struct keymap_key no_latin1_basic[] = { { 0, 0 } }; +/** "no-latin1" AltGr remapping */ +static struct keymap_key no_latin1_altgr[] = { + { 0x30, 0x7d }, /* '0' => '}' */ + { 0x32, 0x40 }, /* '2' => '@' */ + { 0x39, 0x5d }, /* '9' => ']' */ + { 0x5b, 0x7d }, /* '[' => '}' */ + { 0, 0 } +}; + /** "no-latin1" keyboard map */ struct keymap no_latin1_keymap __keymap = { .name = "no-latin1", .basic = no_latin1_basic, + .altgr = no_latin1_altgr, }; diff --git a/src/hci/keymap/keymap_no.c b/src/hci/keymap/keymap_no.c index d3d06bce3..95a95428b 100644 --- a/src/hci/keymap/keymap_no.c +++ b/src/hci/keymap/keymap_no.c @@ -35,8 +35,16 @@ static struct keymap_key no_basic[] = { { 0, 0 } }; +/** "no" AltGr remapping */ +static struct keymap_key no_altgr[] = { + { 0x32, 0x40 }, /* '2' => '@' */ + { 0x71, 0x40 }, /* 'q' => '@' */ + { 0, 0 } +}; + /** "no" keyboard map */ struct keymap no_keymap __keymap = { .name = "no", .basic = no_basic, + .altgr = no_altgr, }; diff --git a/src/hci/keymap/keymap_pl.c b/src/hci/keymap/keymap_pl.c index a23c01f2c..a76181fbc 100644 --- a/src/hci/keymap/keymap_pl.c +++ b/src/hci/keymap/keymap_pl.c @@ -17,8 +17,14 @@ static struct keymap_key pl_basic[] = { { 0, 0 } }; +/** "pl" AltGr remapping */ +static struct keymap_key pl_altgr[] = { + { 0, 0 } +}; + /** "pl" keyboard map */ struct keymap pl_keymap __keymap = { .name = "pl", .basic = pl_basic, + .altgr = pl_altgr, }; diff --git a/src/hci/keymap/keymap_pt.c b/src/hci/keymap/keymap_pt.c index c065fd76f..99ba52e4b 100644 --- a/src/hci/keymap/keymap_pt.c +++ b/src/hci/keymap/keymap_pt.c @@ -34,8 +34,18 @@ static struct keymap_key pt_basic[] = { { 0, 0 } }; +/** "pt" AltGr remapping */ +static struct keymap_key pt_altgr[] = { + { 0x32, 0x40 }, /* '2' => '@' */ + { 0x37, 0x7b }, /* '7' => '{' */ + { 0x38, 0x5b }, /* '8' => '[' */ + { 0x71, 0x40 }, /* 'q' => '@' */ + { 0, 0 } +}; + /** "pt" keyboard map */ struct keymap pt_keymap __keymap = { .name = "pt", .basic = pt_basic, + .altgr = pt_altgr, }; diff --git a/src/hci/keymap/keymap_ro.c b/src/hci/keymap/keymap_ro.c index 334cf6080..620450001 100644 --- a/src/hci/keymap/keymap_ro.c +++ b/src/hci/keymap/keymap_ro.c @@ -15,8 +15,14 @@ static struct keymap_key ro_basic[] = { { 0, 0 } }; +/** "ro" AltGr remapping */ +static struct keymap_key ro_altgr[] = { + { 0, 0 } +}; + /** "ro" keyboard map */ struct keymap ro_keymap __keymap = { .name = "ro", .basic = ro_basic, + .altgr = ro_altgr, }; diff --git a/src/hci/keymap/keymap_ru.c b/src/hci/keymap/keymap_ru.c index a08b115ed..2aafcf9bd 100644 --- a/src/hci/keymap/keymap_ru.c +++ b/src/hci/keymap/keymap_ru.c @@ -18,8 +18,14 @@ static struct keymap_key ru_basic[] = { { 0, 0 } }; +/** "ru" AltGr remapping */ +static struct keymap_key ru_altgr[] = { + { 0, 0 } +}; + /** "ru" keyboard map */ struct keymap ru_keymap __keymap = { .name = "ru", .basic = ru_basic, + .altgr = ru_altgr, }; diff --git a/src/hci/keymap/keymap_sg.c b/src/hci/keymap/keymap_sg.c index 152c5d631..9a6db9cb4 100644 --- a/src/hci/keymap/keymap_sg.c +++ b/src/hci/keymap/keymap_sg.c @@ -43,8 +43,18 @@ static struct keymap_key sg_basic[] = { { 0, 0 } }; +/** "sg" AltGr remapping */ +static struct keymap_key sg_altgr[] = { + { 0x32, 0x40 }, /* '2' => '@' */ + { 0x33, 0x23 }, /* '3' => '#' */ + { 0x37, 0x7c }, /* '7' => '|' */ + { 0x5c, 0x7d }, /* '\\' => '}' */ + { 0, 0 } +}; + /** "sg" keyboard map */ struct keymap sg_keymap __keymap = { .name = "sg", .basic = sg_basic, + .altgr = sg_altgr, }; diff --git a/src/hci/keymap/keymap_sr-latin.c b/src/hci/keymap/keymap_sr-latin.c index ec5efdc89..7e55714a2 100644 --- a/src/hci/keymap/keymap_sr-latin.c +++ b/src/hci/keymap/keymap_sr-latin.c @@ -17,8 +17,14 @@ static struct keymap_key sr_latin_basic[] = { { 0, 0 } }; +/** "sr-latin" AltGr remapping */ +static struct keymap_key sr_latin_altgr[] = { + { 0, 0 } +}; + /** "sr-latin" keyboard map */ struct keymap sr_latin_keymap __keymap = { .name = "sr-latin", .basic = sr_latin_basic, + .altgr = sr_latin_altgr, }; diff --git a/src/hci/keymap/keymap_ua.c b/src/hci/keymap/keymap_ua.c index b4199cdad..44e82cb2d 100644 --- a/src/hci/keymap/keymap_ua.c +++ b/src/hci/keymap/keymap_ua.c @@ -17,8 +17,14 @@ static struct keymap_key ua_basic[] = { { 0, 0 } }; +/** "ua" AltGr remapping */ +static struct keymap_key ua_altgr[] = { + { 0, 0 } +}; + /** "ua" keyboard map */ struct keymap ua_keymap __keymap = { .name = "ua", .basic = ua_basic, + .altgr = ua_altgr, }; diff --git a/src/hci/keymap/keymap_uk.c b/src/hci/keymap/keymap_uk.c index 156b42dff..28cf7aac4 100644 --- a/src/hci/keymap/keymap_uk.c +++ b/src/hci/keymap/keymap_uk.c @@ -19,8 +19,14 @@ static struct keymap_key uk_basic[] = { { 0, 0 } }; +/** "uk" AltGr remapping */ +static struct keymap_key uk_altgr[] = { + { 0, 0 } +}; + /** "uk" keyboard map */ struct keymap uk_keymap __keymap = { .name = "uk", .basic = uk_basic, + .altgr = uk_altgr, }; diff --git a/src/hci/keymap/keymap_us.c b/src/hci/keymap/keymap_us.c index 5d78f80a2..6432474e2 100644 --- a/src/hci/keymap/keymap_us.c +++ b/src/hci/keymap/keymap_us.c @@ -15,8 +15,14 @@ static struct keymap_key us_basic[] = { { 0, 0 } }; +/** "us" AltGr remapping */ +static struct keymap_key us_altgr[] = { + { 0, 0 } +}; + /** "us" keyboard map */ struct keymap us_keymap __keymap = { .name = "us", .basic = us_basic, + .altgr = us_altgr, }; diff --git a/src/include/ipxe/keymap.h b/src/include/ipxe/keymap.h index 3da25190b..72b6961ef 100644 --- a/src/include/ipxe/keymap.h +++ b/src/include/ipxe/keymap.h @@ -32,6 +32,8 @@ struct keymap { const char *name; /** Basic remapping table (zero-terminated) */ struct keymap_key *basic; + /** AltGr remapping table (zero-terminated) */ + struct keymap_key *altgr; }; /** Keyboard mapping table */ @@ -64,6 +66,9 @@ struct keymap { /** Undo and redo CapsLock key flags */ #define KEYMAP_CAPSLOCK_REDO ( KEYMAP_CAPSLOCK | KEYMAP_CAPSLOCK_UNDO ) +/** AltGr key flag */ +#define KEYMAP_ALTGR 0x0800 + extern unsigned int key_remap ( unsigned int character ); #endif /* _IPXE_KEYMAP_H */ diff --git a/src/interface/efi/efi_console.c b/src/interface/efi/efi_console.c index 9adce4a9b..fc1500afb 100644 --- a/src/interface/efi/efi_console.c +++ b/src/interface/efi/efi_console.c @@ -334,6 +334,9 @@ static int efi_getchar ( void ) { EFI_RIGHT_CONTROL_PRESSED ) ) { character |= KEYMAP_CTRL; } + if ( shift & EFI_RIGHT_ALT_PRESSED ) { + character |= KEYMAP_ALTGR; + } } /* Apply toggle state */ diff --git a/src/util/genkeymap.py b/src/util/genkeymap.py index d38552eb4..ff5ff0a87 100755 --- a/src/util/genkeymap.py +++ b/src/util/genkeymap.py @@ -171,6 +171,11 @@ class KeyLayout(UserDict[KeyModifiers, Sequence[Key]]): """Basic shifted keyboard layout""" return self[KeyModifiers.SHIFT] + @property + def altgr(self): + """AltGr keyboard layout""" + return self.get(KeyModifiers.ALTGR, self.unshifted) + @classmethod def load(cls, name: str) -> KeyLayout: """Load keymap using 'loadkeys -b'""" @@ -278,6 +283,7 @@ class KeymapKeys(UserDict[str, str]): self.ascii_name(source), self.ascii_name(target) ) for source, target in self.items() + if ord(source) & ~BiosKeyLayout.KEY_PSEUDO != ord(target) ) + '\t{ 0, 0 }\n}' @@ -301,13 +307,12 @@ class Keymap: raw = {source: self.target[key.modifiers][key.keycode].ascii for source, key in self.source.inverse.items()} # Eliminate any null mappings, mappings that attempt to remap - # the backspace key, or mappings that would become identity - # mappings after clearing the high bit + # the backspace key, or identity mappings table = {source: target for source, target in raw.items() if target and ord(source) != 0x7f and ord(target) != 0x7f - and ord(source) & ~BiosKeyLayout.KEY_PSEUDO != ord(target)} + and source != target} # Recursively delete any mappings that would produce # unreachable alphanumerics (e.g. the "il" keymap, which maps # away the whole lower-case alphabet) @@ -327,6 +332,28 @@ class Keymap: (unshifted, shifted)) return KeymapKeys(dict(sorted(table.items()))) + @property + def altgr(self) -> KeymapKeys: + """AltGr remapping table""" + # Construct raw mapping from source ASCII to target ASCII + raw = {source: self.target.altgr[key.keycode].ascii + for source, key in self.source.inverse.items() + if key.modifiers == KeyModifiers.NONE} + # Identify printable keys that are unreachable via the basic map + basic = self.basic + unmapped = set(x for x in basic.keys() + if x.isascii() and x.isprintable()) + remapped = set(basic.values()) + unreachable = unmapped - remapped + # Eliminate any null mappings, mappings for unprintable + # characters, or mappings for characters that are reachable + # via the basic map + table = {source: target for source, target in raw.items() + if source.isprintable() + and target + and target in unreachable} + return KeymapKeys(dict(sorted(table.items()))) + def cname(self, suffix: str) -> str: """C variable name""" return re.sub(r'\W', '_', (self.name + '_' + suffix)) @@ -336,6 +363,7 @@ class Keymap: """Generated source code""" keymap_name = self.cname("keymap") basic_name = self.cname("basic") + altgr_name = self.cname("altgr") code = textwrap.dedent(f""" /** @file * @@ -352,12 +380,16 @@ class Keymap: /** "{self.name}" basic remapping */ static struct keymap_key {basic_name}[] = %s; + /** "{self.name}" AltGr remapping */ + static struct keymap_key {altgr_name}[] = %s; + /** "{self.name}" keyboard map */ struct keymap {keymap_name} __keymap = {{ \t.name = "{self.name}", \t.basic = {basic_name}, + \t.altgr = {altgr_name}, }}; - """).strip() % self.basic.code + """).strip() % (self.basic.code, self.altgr.code) return code -- cgit v1.2.3-55-g7522 From c7d78192919bfa62fde33650e1506e902816eec3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 15 Feb 2022 11:28:57 +0000 Subject: [console] Treat dead keys as producing their ASCII equivalents Treat dead keys in target keymaps as producing the closest equivalent ASCII character, since many of these characters are otherwise unrepresented on the keyboard. Signed-off-by: Michael Brown --- src/hci/keymap/keymap_al.c | 2 ++ src/hci/keymap/keymap_cf.c | 5 +++++ src/hci/keymap/keymap_cz.c | 6 +++--- src/hci/keymap/keymap_de.c | 2 ++ src/hci/keymap/keymap_dk.c | 3 +++ src/hci/keymap/keymap_es.c | 5 +++++ src/hci/keymap/keymap_et.c | 3 ++- src/hci/keymap/keymap_fi.c | 3 +++ src/hci/keymap/keymap_fr.c | 3 ++- src/hci/keymap/keymap_hu.c | 1 + src/hci/keymap/keymap_it.c | 1 + src/hci/keymap/keymap_nl.c | 4 ++++ src/hci/keymap/keymap_no.c | 3 +++ src/hci/keymap/keymap_pt.c | 5 +++++ src/util/genkeymap.py | 22 ++++++++++++++++++++-- 15 files changed, 61 insertions(+), 7 deletions(-) diff --git a/src/hci/keymap/keymap_al.c b/src/hci/keymap/keymap_al.c index b68b98878..4d279171f 100644 --- a/src/hci/keymap/keymap_al.c +++ b/src/hci/keymap/keymap_al.c @@ -37,7 +37,9 @@ static struct keymap_key al_basic[] = { /** "al" AltGr remapping */ static struct keymap_key al_altgr[] = { + { 0x30, 0x7e }, /* '0' => '~' */ { 0x31, 0x7e }, /* '1' => '~' */ + { 0x34, 0x7e }, /* '4' => '~' */ { 0x37, 0x60 }, /* '7' => '`' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_cf.c b/src/hci/keymap/keymap_cf.c index 09242ee6f..434e3383c 100644 --- a/src/hci/keymap/keymap_cf.c +++ b/src/hci/keymap/keymap_cf.c @@ -12,13 +12,17 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "cf" basic remapping */ static struct keymap_key cf_basic[] = { + { 0x22, 0x60 }, /* '"' => '`' */ { 0x23, 0x2f }, /* '#' => '/' */ + { 0x27, 0x60 }, /* '\'' => '`' */ { 0x3c, 0x27 }, /* '<' => '\'' */ { 0x3e, 0x2e }, /* '>' => '.' */ { 0x40, 0x22 }, /* '@' => '"' */ + { 0x5b, 0x5e }, /* '[' => '^' */ { 0x5c, 0x3c }, /* '\\' => '<' */ { 0x5e, 0x3f }, /* '^' => '?' */ { 0x60, 0x23 }, /* '`' => '#' */ + { 0x7b, 0x5e }, /* '{' => '^' */ { 0x7c, 0x3e }, /* '|' => '>' */ { 0x7e, 0x7c }, /* '~' => '|' */ { 0, 0 } @@ -26,6 +30,7 @@ static struct keymap_key cf_basic[] = { /** "cf" AltGr remapping */ static struct keymap_key cf_altgr[] = { + { 0x27, 0x7b }, /* '\'' => '{' */ { 0x32, 0x40 }, /* '2' => '@' */ { 0x3b, 0x7e }, /* ';' => '~' */ { 0x60, 0x5c }, /* '`' => '\\' */ diff --git a/src/hci/keymap/keymap_cz.c b/src/hci/keymap/keymap_cz.c index cce686d9a..fb072b117 100644 --- a/src/hci/keymap/keymap_cz.c +++ b/src/hci/keymap/keymap_cz.c @@ -24,6 +24,7 @@ static struct keymap_key cz_basic[] = { { 0x28, 0x39 }, /* '(' => '9' */ { 0x29, 0x30 }, /* ')' => '0' */ { 0x2a, 0x38 }, /* '*' => '8' */ + { 0x2b, 0x5e }, /* '+' => '^' */ { 0x2d, 0x3d }, /* '-' => '=' */ { 0x2f, 0x2d }, /* '/' => '-' */ { 0x31, 0x2b }, /* '1' => '+' */ @@ -43,6 +44,7 @@ static struct keymap_key cz_basic[] = { { 0x7b, 0x2f }, /* '{' => '/' */ { 0x7c, 0x27 }, /* '|' => '\'' */ { 0x7d, 0x28 }, /* '}' => '(' */ + { 0x7e, 0x60 }, /* '~' => '`' */ { 0, 0 } }; @@ -55,16 +57,14 @@ static struct keymap_key cz_altgr[] = { { 0x32, 0x40 }, /* '2' => '@' */ { 0x33, 0x23 }, /* '3' => '#' */ { 0x34, 0x24 }, /* '4' => '$' */ - { 0x36, 0x5e }, /* '6' => '^' */ { 0x37, 0x26 }, /* '7' => '&' */ { 0x38, 0x2a }, /* '8' => '*' */ { 0x39, 0x7b }, /* '9' => '{' */ { 0x3b, 0x24 }, /* ';' => '$' */ + { 0x61, 0x7e }, /* 'a' => '~' */ { 0x62, 0x7b }, /* 'b' => '{' */ { 0x63, 0x26 }, /* 'c' => '&' */ { 0x67, 0x5d }, /* 'g' => ']' */ - { 0x68, 0x60 }, /* 'h' => '`' */ - { 0x6d, 0x5e }, /* 'm' => '^' */ { 0x6e, 0x7d }, /* 'n' => '}' */ { 0x76, 0x40 }, /* 'v' => '@' */ { 0x78, 0x23 }, /* 'x' => '#' */ diff --git a/src/hci/keymap/keymap_de.c b/src/hci/keymap/keymap_de.c index 4a889a242..bbd39520f 100644 --- a/src/hci/keymap/keymap_de.c +++ b/src/hci/keymap/keymap_de.c @@ -21,6 +21,7 @@ static struct keymap_key de_basic[] = { { 0x28, 0x29 }, /* '(' => ')' */ { 0x29, 0x3d }, /* ')' => '=' */ { 0x2a, 0x28 }, /* '*' => '(' */ + { 0x2b, 0x60 }, /* '+' => '`' */ { 0x2f, 0x2d }, /* '/' => '-' */ { 0x3c, 0x3b }, /* '<' => ';' */ { 0x3e, 0x3a }, /* '>' => ':' */ @@ -32,6 +33,7 @@ static struct keymap_key de_basic[] = { { 0x5d, 0x2b }, /* ']' => '+' */ { 0x5e, 0x26 }, /* '^' => '&' */ { 0x5f, 0x3f }, /* '_' => '?' */ + { 0x60, 0x5e }, /* '`' => '^' */ { 0x79, 0x7a }, /* 'y' => 'z' */ { 0x7a, 0x79 }, /* 'z' => 'y' */ { 0x7c, 0x27 }, /* '|' => '\'' */ diff --git a/src/hci/keymap/keymap_dk.c b/src/hci/keymap/keymap_dk.c index 4d40743b1..6d2e60aca 100644 --- a/src/hci/keymap/keymap_dk.c +++ b/src/hci/keymap/keymap_dk.c @@ -18,6 +18,7 @@ static struct keymap_key dk_basic[] = { { 0x28, 0x29 }, /* '(' => ')' */ { 0x29, 0x3d }, /* ')' => '=' */ { 0x2a, 0x28 }, /* '*' => '(' */ + { 0x2b, 0x60 }, /* '+' => '`' */ { 0x2d, 0x2b }, /* '-' => '+' */ { 0x2f, 0x2d }, /* '/' => '-' */ { 0x3c, 0x3b }, /* '<' => ';' */ @@ -28,6 +29,7 @@ static struct keymap_key dk_basic[] = { { 0x5e, 0x26 }, /* '^' => '&' */ { 0x5f, 0x3f }, /* '_' => '?' */ { 0x7c, 0x2a }, /* '|' => '*' */ + { 0x7d, 0x5e }, /* '}' => '^' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ { 0, 0 } @@ -35,6 +37,7 @@ static struct keymap_key dk_basic[] = { /** "dk" AltGr remapping */ static struct keymap_key dk_altgr[] = { + { 0x30, 0x7d }, /* '0' => '}' */ { 0x32, 0x40 }, /* '2' => '@' */ { 0x3d, 0x7c }, /* '=' => '|' */ { 0x71, 0x40 }, /* 'q' => '@' */ diff --git a/src/hci/keymap/keymap_es.c b/src/hci/keymap/keymap_es.c index 397e2cbaa..614ca7b8b 100644 --- a/src/hci/keymap/keymap_es.c +++ b/src/hci/keymap/keymap_es.c @@ -24,9 +24,11 @@ static struct keymap_key es_basic[] = { { 0x3e, 0x3a }, /* '>' => ':' */ { 0x3f, 0x5f }, /* '?' => '_' */ { 0x40, 0x22 }, /* '@' => '"' */ + { 0x5b, 0x60 }, /* '[' => '`' */ { 0x5d, 0x2b }, /* ']' => '+' */ { 0x5e, 0x26 }, /* '^' => '&' */ { 0x5f, 0x3f }, /* '_' => '?' */ + { 0x7b, 0x5e }, /* '{' => '^' */ { 0x7d, 0x2a }, /* '}' => '*' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ @@ -35,8 +37,11 @@ static struct keymap_key es_basic[] = { /** "es" AltGr remapping */ static struct keymap_key es_altgr[] = { + { 0x27, 0x7b }, /* '\'' => '{' */ { 0x30, 0x7d }, /* '0' => '}' */ { 0x32, 0x40 }, /* '2' => '@' */ + { 0x37, 0x7b }, /* '7' => '{' */ + { 0x38, 0x5b }, /* '8' => '[' */ { 0x39, 0x5d }, /* '9' => ']' */ { 0x5c, 0x7d }, /* '\\' => '}' */ { 0x71, 0x40 }, /* 'q' => '@' */ diff --git a/src/hci/keymap/keymap_et.c b/src/hci/keymap/keymap_et.c index 4120dbed9..f3eb02eab 100644 --- a/src/hci/keymap/keymap_et.c +++ b/src/hci/keymap/keymap_et.c @@ -16,6 +16,7 @@ static struct keymap_key et_basic[] = { { 0x28, 0x29 }, /* '(' => ')' */ { 0x29, 0x3d }, /* ')' => '=' */ { 0x2a, 0x28 }, /* '*' => '(' */ + { 0x2b, 0x60 }, /* '+' => '`' */ { 0x2d, 0x2b }, /* '-' => '+' */ { 0x2f, 0x2d }, /* '/' => '-' */ { 0x3c, 0x3b }, /* '<' => ';' */ @@ -25,6 +26,7 @@ static struct keymap_key et_basic[] = { { 0x5c, 0x27 }, /* '\\' => '\'' */ { 0x5e, 0x26 }, /* '^' => '&' */ { 0x5f, 0x3f }, /* '_' => '?' */ + { 0x60, 0x5e }, /* '`' => '^' */ { 0x7c, 0x2a }, /* '|' => '*' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ @@ -33,7 +35,6 @@ static struct keymap_key et_basic[] = { /** "et" AltGr remapping */ static struct keymap_key et_altgr[] = { - { 0x27, 0x5e }, /* '\'' => '^' */ { 0x2d, 0x5c }, /* '-' => '\\' */ { 0x32, 0x40 }, /* '2' => '@' */ { 0xdc, 0x7c }, /* Pseudo-'\\' => '|' */ diff --git a/src/hci/keymap/keymap_fi.c b/src/hci/keymap/keymap_fi.c index 978121a88..622f06252 100644 --- a/src/hci/keymap/keymap_fi.c +++ b/src/hci/keymap/keymap_fi.c @@ -16,6 +16,7 @@ static struct keymap_key fi_basic[] = { { 0x28, 0x29 }, /* '(' => ')' */ { 0x29, 0x3d }, /* ')' => '=' */ { 0x2a, 0x28 }, /* '*' => '(' */ + { 0x2b, 0x60 }, /* '+' => '`' */ { 0x2d, 0x2b }, /* '-' => '+' */ { 0x2f, 0x2d }, /* '/' => '-' */ { 0x3c, 0x3b }, /* '<' => ';' */ @@ -26,6 +27,7 @@ static struct keymap_key fi_basic[] = { { 0x5e, 0x26 }, /* '^' => '&' */ { 0x5f, 0x3f }, /* '_' => '?' */ { 0x7c, 0x2a }, /* '|' => '*' */ + { 0x7d, 0x5e }, /* '}' => '^' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ { 0, 0 } @@ -34,6 +36,7 @@ static struct keymap_key fi_basic[] = { /** "fi" AltGr remapping */ static struct keymap_key fi_altgr[] = { { 0x2d, 0x5c }, /* '-' => '\\' */ + { 0x30, 0x7d }, /* '0' => '}' */ { 0x32, 0x40 }, /* '2' => '@' */ { 0xdc, 0x7c }, /* Pseudo-'\\' => '|' */ { 0, 0 } diff --git a/src/hci/keymap/keymap_fr.c b/src/hci/keymap/keymap_fr.c index c0a959f0d..c002bf95a 100644 --- a/src/hci/keymap/keymap_fr.c +++ b/src/hci/keymap/keymap_fr.c @@ -49,6 +49,7 @@ static struct keymap_key fr_basic[] = { { 0x51, 0x41 }, /* 'Q' => 'A' */ { 0x57, 0x5a }, /* 'W' => 'Z' */ { 0x5a, 0x57 }, /* 'Z' => 'W' */ + { 0x5b, 0x5e }, /* '[' => '^' */ { 0x5c, 0x2a }, /* '\\' => '*' */ { 0x5d, 0x24 }, /* ']' => '$' */ { 0x5e, 0x36 }, /* '^' => '6' */ @@ -67,8 +68,8 @@ static struct keymap_key fr_altgr[] = { { 0x2d, 0x5d }, /* '-' => ']' */ { 0x30, 0x40 }, /* '0' => '@' */ { 0x33, 0x23 }, /* '3' => '#' */ + { 0x35, 0x5b }, /* '5' => '[' */ { 0x38, 0x5c }, /* '8' => '\\' */ - { 0x39, 0x5e }, /* '9' => '^' */ { 0x61, 0x40 }, /* 'a' => '@' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_hu.c b/src/hci/keymap/keymap_hu.c index 64e27dda6..8f2d70aac 100644 --- a/src/hci/keymap/keymap_hu.c +++ b/src/hci/keymap/keymap_hu.c @@ -39,6 +39,7 @@ static struct keymap_key hu_basic[] = { static struct keymap_key hu_altgr[] = { { 0x2e, 0x3e }, /* '.' => '>' */ { 0x2f, 0x2a }, /* '/' => '*' */ + { 0x32, 0x5e }, /* '2' => '^' */ { 0x33, 0x5e }, /* '3' => '^' */ { 0x37, 0x60 }, /* '7' => '`' */ { 0x3b, 0x24 }, /* ';' => '$' */ diff --git a/src/hci/keymap/keymap_it.c b/src/hci/keymap/keymap_it.c index f67bbadcb..a55dafe6a 100644 --- a/src/hci/keymap/keymap_it.c +++ b/src/hci/keymap/keymap_it.c @@ -42,6 +42,7 @@ static struct keymap_key it_altgr[] = { { 0x39, 0x5d }, /* '9' => ']' */ { 0x3b, 0x40 }, /* ';' => '@' */ { 0x3d, 0x7e }, /* '=' => '~' */ + { 0x5c, 0x60 }, /* '\\' => '`' */ { 0x71, 0x40 }, /* 'q' => '@' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_nl.c b/src/hci/keymap/keymap_nl.c index bbee4cbdf..8038d4d37 100644 --- a/src/hci/keymap/keymap_nl.c +++ b/src/hci/keymap/keymap_nl.c @@ -15,10 +15,12 @@ static struct keymap_key nl_basic[] = { { 0x1c, 0x3c }, /* 0x1c => '<' */ { 0x1d, 0x1c }, /* 0x1d => 0x1c */ { 0x1e, 0x36 }, /* 0x1e => '6' */ + { 0x22, 0x60 }, /* '"' => '`' */ { 0x26, 0x5f }, /* '&' => '_' */ { 0x28, 0x29 }, /* '(' => ')' */ { 0x29, 0x27 }, /* ')' => '\'' */ { 0x2a, 0x28 }, /* '*' => '(' */ + { 0x2b, 0x7e }, /* '+' => '~' */ { 0x2d, 0x2f }, /* '-' => '/' */ { 0x2f, 0x2d }, /* '/' => '-' */ { 0x3b, 0x2b }, /* ';' => '+' */ @@ -31,6 +33,7 @@ static struct keymap_key nl_basic[] = { { 0x5e, 0x26 }, /* '^' => '&' */ { 0x5f, 0x3f }, /* '_' => '?' */ { 0x60, 0x40 }, /* '`' => '@' */ + { 0x7b, 0x5e }, /* '{' => '^' */ { 0x7c, 0x3e }, /* '|' => '>' */ { 0x7d, 0x7c }, /* '}' => '|' */ { 0xdc, 0x5d }, /* Pseudo-'\\' => ']' */ @@ -41,6 +44,7 @@ static struct keymap_key nl_basic[] = { /** "nl" AltGr remapping */ static struct keymap_key nl_altgr[] = { { 0x2d, 0x5c }, /* '-' => '\\' */ + { 0x38, 0x7b }, /* '8' => '{' */ { 0x39, 0x7d }, /* '9' => '}' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_no.c b/src/hci/keymap/keymap_no.c index 95a95428b..4b2f7268e 100644 --- a/src/hci/keymap/keymap_no.c +++ b/src/hci/keymap/keymap_no.c @@ -18,6 +18,7 @@ static struct keymap_key no_basic[] = { { 0x28, 0x29 }, /* '(' => ')' */ { 0x29, 0x3d }, /* ')' => '=' */ { 0x2a, 0x28 }, /* '*' => '(' */ + { 0x2b, 0x60 }, /* '+' => '`' */ { 0x2d, 0x2b }, /* '-' => '+' */ { 0x2f, 0x2d }, /* '/' => '-' */ { 0x3c, 0x3b }, /* '<' => ';' */ @@ -30,6 +31,7 @@ static struct keymap_key no_basic[] = { { 0x5f, 0x3f }, /* '_' => '?' */ { 0x60, 0x7c }, /* '`' => '|' */ { 0x7c, 0x2a }, /* '|' => '*' */ + { 0x7d, 0x5e }, /* '}' => '^' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ { 0, 0 } @@ -37,6 +39,7 @@ static struct keymap_key no_basic[] = { /** "no" AltGr remapping */ static struct keymap_key no_altgr[] = { + { 0x30, 0x7d }, /* '0' => '}' */ { 0x32, 0x40 }, /* '2' => '@' */ { 0x71, 0x40 }, /* 'q' => '@' */ { 0, 0 } diff --git a/src/hci/keymap/keymap_pt.c b/src/hci/keymap/keymap_pt.c index 99ba52e4b..f294e9563 100644 --- a/src/hci/keymap/keymap_pt.c +++ b/src/hci/keymap/keymap_pt.c @@ -12,6 +12,7 @@ FILE_LICENCE ( PUBLIC_DOMAIN ); /** "pt" basic remapping */ static struct keymap_key pt_basic[] = { + { 0x1c, 0x7e }, /* 0x1c => '~' */ { 0x1e, 0x36 }, /* 0x1e => '6' */ { 0x26, 0x2f }, /* '&' => '/' */ { 0x28, 0x29 }, /* '(' => ')' */ @@ -24,10 +25,13 @@ static struct keymap_key pt_basic[] = { { 0x3f, 0x5f }, /* '?' => '_' */ { 0x40, 0x22 }, /* '@' => '"' */ { 0x5b, 0x2b }, /* '[' => '+' */ + { 0x5c, 0x7e }, /* '\\' => '~' */ { 0x5e, 0x26 }, /* '^' => '&' */ { 0x5f, 0x3f }, /* '_' => '?' */ { 0x60, 0x5c }, /* '`' => '\\' */ { 0x7b, 0x2a }, /* '{' => '*' */ + { 0x7c, 0x5e }, /* '|' => '^' */ + { 0x7d, 0x60 }, /* '}' => '`' */ { 0x7e, 0x7c }, /* '~' => '|' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ @@ -36,6 +40,7 @@ static struct keymap_key pt_basic[] = { /** "pt" AltGr remapping */ static struct keymap_key pt_altgr[] = { + { 0x30, 0x7d }, /* '0' => '}' */ { 0x32, 0x40 }, /* '2' => '@' */ { 0x37, 0x7b }, /* '7' => '{' */ { 0x38, 0x5b }, /* '8' => '[' */ diff --git a/src/util/genkeymap.py b/src/util/genkeymap.py index ff5ff0a87..42ccee175 100755 --- a/src/util/genkeymap.py +++ b/src/util/genkeymap.py @@ -54,6 +54,14 @@ class KeyType(IntEnum): UNKNOWN = 0xf0 +class DeadKey(IntEnum): + """Dead keys""" + + GRAVE = 0 + CIRCUMFLEX = 2 + TILDE = 3 + + class KeyModifiers(Flag): """Key modifiers""" @@ -96,6 +104,13 @@ class Key: KeyType.LETTER} """Key types with direct ASCII values""" + DEAD_KEYS: ClassVar[Mapping[int, str]] = { + DeadKey.GRAVE: '`', + DeadKey.CIRCUMFLEX: '^', + DeadKey.TILDE: '~', + } + """Dead key replacement ASCII values""" + @property def keytype(self) -> Optional[KeyType]: """Key type""" @@ -112,11 +127,14 @@ class Key: @property def ascii(self) -> Optional[str]: """ASCII character""" - if self.keytype in self.ASCII_TYPES: - value = self.value + keytype = self.keytype + value = self.value + if keytype in self.ASCII_TYPES: char = chr(value) if value and char.isascii(): return char + if keytype == KeyType.DEAD: + return self.DEAD_KEYS.get(value) return None -- cgit v1.2.3-55-g7522 From 164db2cc630932a91119b1aee5fa299d17716819 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 15 Feb 2022 11:43:08 +0000 Subject: [console] Fix unreachable characters in "il" keymap Signed-off-by: Michael Brown --- src/hci/keymap/keymap_il.c | 1 + src/util/genkeymap.py | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/hci/keymap/keymap_il.c b/src/hci/keymap/keymap_il.c index e3061fa54..95e33a0bd 100644 --- a/src/hci/keymap/keymap_il.c +++ b/src/hci/keymap/keymap_il.c @@ -31,6 +31,7 @@ static struct keymap_key il_basic[] = { /** "il" AltGr remapping */ static struct keymap_key il_altgr[] = { + { 0x5c, 0x60 }, /* '\\' => '`' */ { 0, 0 } }; diff --git a/src/util/genkeymap.py b/src/util/genkeymap.py index 42ccee175..5f87a89d2 100755 --- a/src/util/genkeymap.py +++ b/src/util/genkeymap.py @@ -176,6 +176,12 @@ class KeyLayout(UserDict[KeyModifiers, Sequence[Key]]): # Treat Ctrl-Backspace as producing Backspace rather than Ctrl-H KeyModifiers.CTRL: [(KEY_BACKSPACE, 0x7f)], }, + 'il': { + # Redefine some otherwise unreachable ASCII characters + # using the closest available approximation + KeyModifiers.ALTGR: [(0x28, ord('\'')), (0x2b, ord('`')), + (0x35, ord('/'))], + }, } """Fixups for erroneous keymappings produced by 'loadkeys -b'""" -- cgit v1.2.3-55-g7522 From a7a79ab12b1ba7b3873fccaa4778443c1577ad8e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 15 Feb 2022 11:45:17 +0000 Subject: [console] Fix unreachable characters in "mt" keymap Signed-off-by: Michael Brown --- src/hci/keymap/keymap_mt.c | 1 - src/util/genkeymap.py | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/hci/keymap/keymap_mt.c b/src/hci/keymap/keymap_mt.c index f5baf6907..0a9a110b0 100644 --- a/src/hci/keymap/keymap_mt.c +++ b/src/hci/keymap/keymap_mt.c @@ -22,7 +22,6 @@ static struct keymap_key mt_basic[] = { /** "mt" AltGr remapping */ static struct keymap_key mt_altgr[] = { - { 0x2d, 0x5c }, /* '-' => '\\' */ { 0, 0 } }; diff --git a/src/util/genkeymap.py b/src/util/genkeymap.py index 5f87a89d2..e471cd31a 100755 --- a/src/util/genkeymap.py +++ b/src/util/genkeymap.py @@ -182,6 +182,11 @@ class KeyLayout(UserDict[KeyModifiers, Sequence[Key]]): KeyModifiers.ALTGR: [(0x28, ord('\'')), (0x2b, ord('`')), (0x35, ord('/'))], }, + 'mt': { + # Redefine erroneous key 86 as generating "\\|" + KeyModifiers.NONE: [(KEY_NON_US, ord('\\'))], + KeyModifiers.SHIFT: [(KEY_NON_US, ord('|'))], + }, } """Fixups for erroneous keymappings produced by 'loadkeys -b'""" -- cgit v1.2.3-55-g7522 From 429d4beb8946b9163643be9af9b7a0619390737a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 15 Feb 2022 11:53:07 +0000 Subject: [console] Remove "az" keymap The "az" keymap has several unreachable ASCII characters, with no obvious closest equivalent keys. Signed-off-by: Michael Brown --- src/hci/keymap/keymap_az.c | 40 ---------------------------------------- 1 file changed, 40 deletions(-) delete mode 100644 src/hci/keymap/keymap_az.c diff --git a/src/hci/keymap/keymap_az.c b/src/hci/keymap/keymap_az.c deleted file mode 100644 index 03087e01e..000000000 --- a/src/hci/keymap/keymap_az.c +++ /dev/null @@ -1,40 +0,0 @@ -/** @file - * - * "az" keyboard mapping - * - * This file is automatically generated; do not edit - * - */ - -FILE_LICENCE ( PUBLIC_DOMAIN ); - -#include - -/** "az" basic remapping */ -static struct keymap_key az_basic[] = { - { 0x1e, 0x36 }, /* 0x1e => '6' */ - { 0x24, 0x3b }, /* '$' => ';' */ - { 0x26, 0x3f }, /* '&' => '?' */ - { 0x2f, 0x2e }, /* '/' => '.' */ - { 0x3a, 0x49 }, /* ':' => 'I' */ - { 0x3f, 0x2c }, /* '?' => ',' */ - { 0x40, 0x22 }, /* '@' => '"' */ - { 0x5e, 0x3a }, /* '^' => ':' */ - { 0x7c, 0x2f }, /* '|' => '/' */ - { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ - { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ - { 0, 0 } -}; - -/** "az" AltGr remapping */ -static struct keymap_key az_altgr[] = { - { 0xdc, 0x7c }, /* Pseudo-'\\' => '|' */ - { 0, 0 } -}; - -/** "az" keyboard map */ -struct keymap az_keymap __keymap = { - .name = "az", - .basic = az_basic, - .altgr = az_altgr, -}; -- cgit v1.2.3-55-g7522 From 510f9de0a23d3a64654669e1a08ad7fdba5f270b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 15 Feb 2022 11:51:37 +0000 Subject: [console] Ensure that all ASCII characters are reachable in all keymaps Signed-off-by: Michael Brown --- src/util/genkeymap.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/util/genkeymap.py b/src/util/genkeymap.py index e471cd31a..8df949612 100755 --- a/src/util/genkeymap.py +++ b/src/util/genkeymap.py @@ -381,6 +381,12 @@ class Keymap: if source.isprintable() and target and target in unreachable} + # Check that all characters are now reachable + unreachable -= set(table.values()) + if unreachable: + raise ValueError("Unreachable characters: %s" % ', '.join( + KeymapKeys.ascii_name(x) for x in sorted(unreachable) + )) return KeymapKeys(dict(sorted(table.items()))) def cname(self, suffix: str) -> str: -- cgit v1.2.3-55-g7522 From 5aee6b81d73086fd4a48f2280043d7b5518b3303 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 15 Feb 2022 13:54:28 +0000 Subject: [build] Avoid invoking genkeymap.py via Perl The build process currently invokes the Python genkeymap.py script via the Perl executable. Strangely, this appears to work. Signed-off-by: Michael Brown --- src/Makefile | 1 + src/Makefile.housekeeping | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 83642e789..bc82cc6fe 100644 --- a/src/Makefile +++ b/src/Makefile @@ -24,6 +24,7 @@ CP := cp ECHO := echo PRINTF := printf PERL := perl +PYTHON := python TRUE := true CC := $(CROSS_COMPILE)gcc CPP := $(CC) -E diff --git a/src/Makefile.housekeeping b/src/Makefile.housekeeping index 24ed5d13b..9bf34b2e6 100644 --- a/src/Makefile.housekeeping +++ b/src/Makefile.housekeeping @@ -1563,7 +1563,7 @@ endif # defined(BIN) # hci/keymap/keymap_%.c : - $(Q)$(PERL) $(GENKEYMAP) $* > $@ + $(Q)$(PYTHON) $(GENKEYMAP) $* > $@ ############################################################################### # -- cgit v1.2.3-55-g7522 From 4a37b050081cbb9ea384a203502eca09a2d8038c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 15 Feb 2022 14:12:18 +0000 Subject: [console] Add Swedish "se" keymap Signed-off-by: Michael Brown --- src/hci/keymap/keymap_se.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/hci/keymap/keymap_se.c diff --git a/src/hci/keymap/keymap_se.c b/src/hci/keymap/keymap_se.c new file mode 100644 index 000000000..6ca990444 --- /dev/null +++ b/src/hci/keymap/keymap_se.c @@ -0,0 +1,53 @@ +/** @file + * + * "se" keyboard mapping + * + * This file is automatically generated; do not edit + * + */ + +FILE_LICENCE ( PUBLIC_DOMAIN ); + +#include + +/** "se" basic remapping */ +static struct keymap_key se_basic[] = { + { 0x1c, 0x27 }, /* 0x1c => '\'' */ + { 0x1e, 0x36 }, /* 0x1e => '6' */ + { 0x26, 0x2f }, /* '&' => '/' */ + { 0x28, 0x29 }, /* '(' => ')' */ + { 0x29, 0x3d }, /* ')' => '=' */ + { 0x2a, 0x28 }, /* '*' => '(' */ + { 0x2b, 0x60 }, /* '+' => '`' */ + { 0x2d, 0x2b }, /* '-' => '+' */ + { 0x2f, 0x2d }, /* '/' => '-' */ + { 0x3c, 0x3b }, /* '<' => ';' */ + { 0x3e, 0x3a }, /* '>' => ':' */ + { 0x3f, 0x5f }, /* '?' => '_' */ + { 0x40, 0x22 }, /* '@' => '"' */ + { 0x5c, 0x27 }, /* '\\' => '\'' */ + { 0x5e, 0x26 }, /* '^' => '&' */ + { 0x5f, 0x3f }, /* '_' => '?' */ + { 0x7c, 0x2a }, /* '|' => '*' */ + { 0x7d, 0x5e }, /* '}' => '^' */ + { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ + { 0, 0 } +}; + +/** "se" AltGr remapping */ +static struct keymap_key se_altgr[] = { + { 0x2d, 0x5c }, /* '-' => '\\' */ + { 0x30, 0x7d }, /* '0' => '}' */ + { 0x32, 0x40 }, /* '2' => '@' */ + { 0x71, 0x40 }, /* 'q' => '@' */ + { 0xdc, 0x7c }, /* Pseudo-'\\' => '|' */ + { 0, 0 } +}; + +/** "se" keyboard map */ +struct keymap se_keymap __keymap = { + .name = "se", + .basic = se_basic, + .altgr = se_altgr, +}; -- cgit v1.2.3-55-g7522 From 419b2e71da689ded944c3789c87b7ae3646c7a7c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 15 Feb 2022 16:41:19 +0000 Subject: [console] Fix definition of unreachability for remapped keys The AltGr remapping table is constructed to include only keys that are not reachable after applying the basic remapping table. The logic currently fails to include keys that are omitted entirely from the basic remapping table since they would map to a non-ASCII character. Fix this logic by allowing the remapping tables to include null mappings, which are then elided only at the point of constructing the C code fragment. Reported-by: Christian Nilsson Signed-off-by: Michael Brown --- src/hci/keymap/keymap_al.c | 5 +++++ src/hci/keymap/keymap_cf.c | 1 + src/hci/keymap/keymap_cz.c | 8 +++++++ src/hci/keymap/keymap_de.c | 5 +++++ src/hci/keymap/keymap_dk.c | 7 ++++++ src/hci/keymap/keymap_es.c | 13 +++++++++++ src/hci/keymap/keymap_et.c | 10 +++++++++ src/hci/keymap/keymap_fi.c | 6 +++++ src/hci/keymap/keymap_fr.c | 6 +++++ src/hci/keymap/keymap_hu.c | 18 +++++++++++++++ src/hci/keymap/keymap_it.c | 6 +++++ src/hci/keymap/keymap_lt.c | 4 ++++ src/hci/keymap/keymap_mt.c | 5 +++++ src/hci/keymap/keymap_no-latin1.c | 5 +++++ src/hci/keymap/keymap_no.c | 7 ++++++ src/hci/keymap/keymap_pt.c | 1 + src/hci/keymap/keymap_se.c | 5 +++++ src/hci/keymap/keymap_sg.c | 2 ++ src/util/genkeymap.py | 46 +++++++++++++++++++-------------------- 19 files changed, 137 insertions(+), 23 deletions(-) diff --git a/src/hci/keymap/keymap_al.c b/src/hci/keymap/keymap_al.c index 4d279171f..6e14395a5 100644 --- a/src/hci/keymap/keymap_al.c +++ b/src/hci/keymap/keymap_al.c @@ -37,10 +37,15 @@ static struct keymap_key al_basic[] = { /** "al" AltGr remapping */ static struct keymap_key al_altgr[] = { + { 0x21, 0x7e }, /* '!' => '~' */ + { 0x26, 0x60 }, /* '&' => '`' */ { 0x30, 0x7e }, /* '0' => '~' */ { 0x31, 0x7e }, /* '1' => '~' */ { 0x34, 0x7e }, /* '4' => '~' */ { 0x37, 0x60 }, /* '7' => '`' */ + { 0x3a, 0x7e }, /* ':' => '~' */ + { 0x56, 0x60 }, /* 'V' => '`' */ + { 0x7c, 0x7e }, /* '|' => '~' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_cf.c b/src/hci/keymap/keymap_cf.c index 434e3383c..55bbfc1a4 100644 --- a/src/hci/keymap/keymap_cf.c +++ b/src/hci/keymap/keymap_cf.c @@ -33,6 +33,7 @@ static struct keymap_key cf_altgr[] = { { 0x27, 0x7b }, /* '\'' => '{' */ { 0x32, 0x40 }, /* '2' => '@' */ { 0x3b, 0x7e }, /* ';' => '~' */ + { 0x5c, 0x7d }, /* '\\' => '}' */ { 0x60, 0x5c }, /* '`' => '\\' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_cz.c b/src/hci/keymap/keymap_cz.c index fb072b117..08e9638e1 100644 --- a/src/hci/keymap/keymap_cz.c +++ b/src/hci/keymap/keymap_cz.c @@ -50,6 +50,9 @@ static struct keymap_key cz_basic[] = { /** "cz" AltGr remapping */ static struct keymap_key cz_altgr[] = { + { 0x21, 0x7e }, /* '!' => '~' */ + { 0x24, 0x7e }, /* '$' => '~' */ + { 0x29, 0x7e }, /* ')' => '~' */ { 0x2c, 0x3c }, /* ',' => '<' */ { 0x2e, 0x3e }, /* '.' => '>' */ { 0x2f, 0x2a }, /* '/' => '*' */ @@ -60,10 +63,15 @@ static struct keymap_key cz_altgr[] = { { 0x37, 0x26 }, /* '7' => '&' */ { 0x38, 0x2a }, /* '8' => '*' */ { 0x39, 0x7b }, /* '9' => '{' */ + { 0x3a, 0x7e }, /* ':' => '~' */ { 0x3b, 0x24 }, /* ';' => '$' */ + { 0x4b, 0x26 }, /* 'K' => '&' */ + { 0x58, 0x3e }, /* 'X' => '>' */ + { 0x5a, 0x3c }, /* 'Z' => '<' */ { 0x61, 0x7e }, /* 'a' => '~' */ { 0x62, 0x7b }, /* 'b' => '{' */ { 0x63, 0x26 }, /* 'c' => '&' */ + { 0x66, 0x5b }, /* 'f' => '[' */ { 0x67, 0x5d }, /* 'g' => ']' */ { 0x6e, 0x7d }, /* 'n' => '}' */ { 0x76, 0x40 }, /* 'v' => '@' */ diff --git a/src/hci/keymap/keymap_de.c b/src/hci/keymap/keymap_de.c index bbd39520f..ca5e4a9a9 100644 --- a/src/hci/keymap/keymap_de.c +++ b/src/hci/keymap/keymap_de.c @@ -47,8 +47,13 @@ static struct keymap_key de_basic[] = { static struct keymap_key de_altgr[] = { { 0x2d, 0x5c }, /* '-' => '\\' */ { 0x30, 0x7d }, /* '0' => '}' */ + { 0x37, 0x7b }, /* '7' => '{' */ + { 0x38, 0x5b }, /* '8' => '[' */ { 0x39, 0x5d }, /* '9' => ']' */ + { 0x3b, 0x7e }, /* ';' => '~' */ + { 0x5d, 0x7e }, /* ']' => '~' */ { 0x71, 0x40 }, /* 'q' => '@' */ + { 0x7c, 0x7e }, /* '|' => '~' */ { 0xdc, 0x7c }, /* Pseudo-'\\' => '|' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_dk.c b/src/hci/keymap/keymap_dk.c index 6d2e60aca..5b229024a 100644 --- a/src/hci/keymap/keymap_dk.c +++ b/src/hci/keymap/keymap_dk.c @@ -39,7 +39,14 @@ static struct keymap_key dk_basic[] = { static struct keymap_key dk_altgr[] = { { 0x30, 0x7d }, /* '0' => '}' */ { 0x32, 0x40 }, /* '2' => '@' */ + { 0x34, 0x24 }, /* '4' => '$' */ + { 0x37, 0x7b }, /* '7' => '{' */ + { 0x38, 0x5b }, /* '8' => '[' */ + { 0x39, 0x5d }, /* '9' => ']' */ + { 0x3a, 0x7e }, /* ':' => '~' */ { 0x3d, 0x7c }, /* '=' => '|' */ + { 0x5c, 0x7e }, /* '\\' => '~' */ + { 0x5d, 0x7e }, /* ']' => '~' */ { 0x71, 0x40 }, /* 'q' => '@' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_es.c b/src/hci/keymap/keymap_es.c index 614ca7b8b..93d87f2a0 100644 --- a/src/hci/keymap/keymap_es.c +++ b/src/hci/keymap/keymap_es.c @@ -37,14 +37,27 @@ static struct keymap_key es_basic[] = { /** "es" AltGr remapping */ static struct keymap_key es_altgr[] = { + { 0x22, 0x7b }, /* '"' => '{' */ { 0x27, 0x7b }, /* '\'' => '{' */ + { 0x2b, 0x7e }, /* '+' => '~' */ + { 0x2d, 0x5c }, /* '-' => '\\' */ { 0x30, 0x7d }, /* '0' => '}' */ + { 0x31, 0x7c }, /* '1' => '|' */ { 0x32, 0x40 }, /* '2' => '@' */ + { 0x33, 0x23 }, /* '3' => '#' */ + { 0x34, 0x7e }, /* '4' => '~' */ { 0x37, 0x7b }, /* '7' => '{' */ { 0x38, 0x5b }, /* '8' => '[' */ { 0x39, 0x5d }, /* '9' => ']' */ + { 0x3a, 0x7e }, /* ':' => '~' */ + { 0x3b, 0x7e }, /* ';' => '~' */ + { 0x3d, 0x7e }, /* '=' => '~' */ { 0x5c, 0x7d }, /* '\\' => '}' */ + { 0x60, 0x5c }, /* '`' => '\\' */ { 0x71, 0x40 }, /* 'q' => '@' */ + { 0x7c, 0x7e }, /* '|' => '~' */ + { 0x7e, 0x5c }, /* '~' => '\\' */ + { 0xdc, 0x7c }, /* Pseudo-'\\' => '|' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_et.c b/src/hci/keymap/keymap_et.c index f3eb02eab..4d763266f 100644 --- a/src/hci/keymap/keymap_et.c +++ b/src/hci/keymap/keymap_et.c @@ -35,8 +35,18 @@ static struct keymap_key et_basic[] = { /** "et" AltGr remapping */ static struct keymap_key et_altgr[] = { + { 0x26, 0x7b }, /* '&' => '{' */ + { 0x28, 0x5d }, /* '(' => ']' */ + { 0x29, 0x7d }, /* ')' => '}' */ + { 0x2a, 0x5b }, /* '*' => '[' */ { 0x2d, 0x5c }, /* '-' => '\\' */ + { 0x30, 0x7d }, /* '0' => '}' */ { 0x32, 0x40 }, /* '2' => '@' */ + { 0x34, 0x24 }, /* '4' => '$' */ + { 0x37, 0x7b }, /* '7' => '{' */ + { 0x38, 0x5b }, /* '8' => '[' */ + { 0x39, 0x5d }, /* '9' => ']' */ + { 0x5f, 0x5c }, /* '_' => '\\' */ { 0xdc, 0x7c }, /* Pseudo-'\\' => '|' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_fi.c b/src/hci/keymap/keymap_fi.c index 622f06252..ac69813e8 100644 --- a/src/hci/keymap/keymap_fi.c +++ b/src/hci/keymap/keymap_fi.c @@ -38,6 +38,12 @@ static struct keymap_key fi_altgr[] = { { 0x2d, 0x5c }, /* '-' => '\\' */ { 0x30, 0x7d }, /* '0' => '}' */ { 0x32, 0x40 }, /* '2' => '@' */ + { 0x34, 0x24 }, /* '4' => '$' */ + { 0x37, 0x7b }, /* '7' => '{' */ + { 0x38, 0x5b }, /* '8' => '[' */ + { 0x39, 0x5d }, /* '9' => ']' */ + { 0x49, 0x7c }, /* 'I' => '|' */ + { 0x5d, 0x7e }, /* ']' => '~' */ { 0xdc, 0x7c }, /* Pseudo-'\\' => '|' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_fr.c b/src/hci/keymap/keymap_fr.c index c002bf95a..ad797f876 100644 --- a/src/hci/keymap/keymap_fr.c +++ b/src/hci/keymap/keymap_fr.c @@ -68,9 +68,15 @@ static struct keymap_key fr_altgr[] = { { 0x2d, 0x5d }, /* '-' => ']' */ { 0x30, 0x40 }, /* '0' => '@' */ { 0x33, 0x23 }, /* '3' => '#' */ + { 0x34, 0x7b }, /* '4' => '{' */ { 0x35, 0x5b }, /* '5' => '[' */ + { 0x36, 0x7c }, /* '6' => '|' */ + { 0x37, 0x60 }, /* '7' => '`' */ { 0x38, 0x5c }, /* '8' => '\\' */ + { 0x3d, 0x7d }, /* '=' => '}' */ + { 0x5c, 0x60 }, /* '\\' => '`' */ { 0x61, 0x40 }, /* 'a' => '@' */ + { 0xdc, 0x7c }, /* Pseudo-'\\' => '|' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_hu.c b/src/hci/keymap/keymap_hu.c index 8f2d70aac..d64baaebf 100644 --- a/src/hci/keymap/keymap_hu.c +++ b/src/hci/keymap/keymap_hu.c @@ -37,18 +37,36 @@ static struct keymap_key hu_basic[] = { /** "hu" AltGr remapping */ static struct keymap_key hu_altgr[] = { + { 0x21, 0x7e }, /* '!' => '~' */ + { 0x23, 0x5e }, /* '#' => '^' */ + { 0x26, 0x60 }, /* '&' => '`' */ + { 0x2c, 0x3b }, /* ',' => ';' */ { 0x2e, 0x3e }, /* '.' => '>' */ { 0x2f, 0x2a }, /* '/' => '*' */ + { 0x30, 0x7e }, /* '0' => '~' */ + { 0x31, 0x7e }, /* '1' => '~' */ { 0x32, 0x5e }, /* '2' => '^' */ { 0x33, 0x5e }, /* '3' => '^' */ + { 0x34, 0x7e }, /* '4' => '~' */ { 0x37, 0x60 }, /* '7' => '`' */ { 0x3b, 0x24 }, /* ';' => '$' */ + { 0x4b, 0x26 }, /* 'K' => '&' */ + { 0x58, 0x3e }, /* 'X' => '>' */ + { 0x5a, 0x3c }, /* 'Z' => '<' */ + { 0x62, 0x7b }, /* 'b' => '{' */ { 0x63, 0x26 }, /* 'c' => '&' */ + { 0x66, 0x5b }, /* 'f' => '[' */ + { 0x67, 0x5d }, /* 'g' => ']' */ { 0x6d, 0x3c }, /* 'm' => '<' */ + { 0x6e, 0x7d }, /* 'n' => '}' */ + { 0x71, 0x5c }, /* 'q' => '\\' */ { 0x76, 0x40 }, /* 'v' => '@' */ + { 0x77, 0x7c }, /* 'w' => '|' */ { 0x78, 0x23 }, /* 'x' => '#' */ { 0x7a, 0x3e }, /* 'z' => '>' */ + { 0x7c, 0x7e }, /* '|' => '~' */ { 0xdc, 0x3c }, /* Pseudo-'\\' => '<' */ + { 0xfc, 0x3e }, /* Pseudo-'|' => '>' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_it.c b/src/hci/keymap/keymap_it.c index a55dafe6a..2511bb791 100644 --- a/src/hci/keymap/keymap_it.c +++ b/src/hci/keymap/keymap_it.c @@ -37,13 +37,19 @@ static struct keymap_key it_basic[] = { /** "it" AltGr remapping */ static struct keymap_key it_altgr[] = { + { 0x23, 0x7e }, /* '#' => '~' */ + { 0x27, 0x23 }, /* '\'' => '#' */ { 0x2d, 0x60 }, /* '-' => '`' */ { 0x30, 0x7d }, /* '0' => '}' */ + { 0x37, 0x7b }, /* '7' => '{' */ + { 0x38, 0x5b }, /* '8' => '[' */ { 0x39, 0x5d }, /* '9' => ']' */ { 0x3b, 0x40 }, /* ';' => '@' */ { 0x3d, 0x7e }, /* '=' => '~' */ + { 0x40, 0x7e }, /* '@' => '~' */ { 0x5c, 0x60 }, /* '\\' => '`' */ { 0x71, 0x40 }, /* 'q' => '@' */ + { 0x7c, 0x7e }, /* '|' => '~' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_lt.c b/src/hci/keymap/keymap_lt.c index 5d6ee5a8c..a237b1d62 100644 --- a/src/hci/keymap/keymap_lt.c +++ b/src/hci/keymap/keymap_lt.c @@ -17,6 +17,10 @@ static struct keymap_key lt_basic[] = { /** "lt" AltGr remapping */ static struct keymap_key lt_altgr[] = { + { 0x22, 0x5e }, /* '"' => '^' */ + { 0x27, 0x5e }, /* '\'' => '^' */ + { 0x4b, 0x26 }, /* 'K' => '&' */ + { 0x71, 0x40 }, /* 'q' => '@' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_mt.c b/src/hci/keymap/keymap_mt.c index 0a9a110b0..a95c5b4e8 100644 --- a/src/hci/keymap/keymap_mt.c +++ b/src/hci/keymap/keymap_mt.c @@ -22,6 +22,11 @@ static struct keymap_key mt_basic[] = { /** "mt" AltGr remapping */ static struct keymap_key mt_altgr[] = { + { 0x30, 0x7d }, /* '0' => '}' */ + { 0x37, 0x7b }, /* '7' => '{' */ + { 0x38, 0x5b }, /* '8' => '[' */ + { 0x39, 0x5d }, /* '9' => ']' */ + { 0x5c, 0x60 }, /* '\\' => '`' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_no-latin1.c b/src/hci/keymap/keymap_no-latin1.c index 63fe85548..1f4cc38d3 100644 --- a/src/hci/keymap/keymap_no-latin1.c +++ b/src/hci/keymap/keymap_no-latin1.c @@ -39,10 +39,15 @@ static struct keymap_key no_latin1_basic[] = { /** "no-latin1" AltGr remapping */ static struct keymap_key no_latin1_altgr[] = { + { 0x22, 0x5b }, /* '"' => '[' */ + { 0x27, 0x7b }, /* '\'' => '{' */ { 0x30, 0x7d }, /* '0' => '}' */ { 0x32, 0x40 }, /* '2' => '@' */ + { 0x37, 0x7b }, /* '7' => '{' */ + { 0x38, 0x5b }, /* '8' => '[' */ { 0x39, 0x5d }, /* '9' => ']' */ { 0x5b, 0x7d }, /* '[' => '}' */ + { 0x7b, 0x5d }, /* '{' => ']' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_no.c b/src/hci/keymap/keymap_no.c index 4b2f7268e..239e3aea5 100644 --- a/src/hci/keymap/keymap_no.c +++ b/src/hci/keymap/keymap_no.c @@ -41,6 +41,13 @@ static struct keymap_key no_basic[] = { static struct keymap_key no_altgr[] = { { 0x30, 0x7d }, /* '0' => '}' */ { 0x32, 0x40 }, /* '2' => '@' */ + { 0x34, 0x24 }, /* '4' => '$' */ + { 0x37, 0x7b }, /* '7' => '{' */ + { 0x38, 0x5b }, /* '8' => '[' */ + { 0x39, 0x5d }, /* '9' => ']' */ + { 0x3a, 0x7e }, /* ':' => '~' */ + { 0x5c, 0x7e }, /* '\\' => '~' */ + { 0x5d, 0x7e }, /* ']' => '~' */ { 0x71, 0x40 }, /* 'q' => '@' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_pt.c b/src/hci/keymap/keymap_pt.c index f294e9563..75bf565ce 100644 --- a/src/hci/keymap/keymap_pt.c +++ b/src/hci/keymap/keymap_pt.c @@ -44,6 +44,7 @@ static struct keymap_key pt_altgr[] = { { 0x32, 0x40 }, /* '2' => '@' */ { 0x37, 0x7b }, /* '7' => '{' */ { 0x38, 0x5b }, /* '8' => '[' */ + { 0x39, 0x5d }, /* '9' => ']' */ { 0x71, 0x40 }, /* 'q' => '@' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_se.c b/src/hci/keymap/keymap_se.c index 6ca990444..9c6cf64a4 100644 --- a/src/hci/keymap/keymap_se.c +++ b/src/hci/keymap/keymap_se.c @@ -40,6 +40,11 @@ static struct keymap_key se_altgr[] = { { 0x2d, 0x5c }, /* '-' => '\\' */ { 0x30, 0x7d }, /* '0' => '}' */ { 0x32, 0x40 }, /* '2' => '@' */ + { 0x34, 0x24 }, /* '4' => '$' */ + { 0x37, 0x7b }, /* '7' => '{' */ + { 0x38, 0x5b }, /* '8' => '[' */ + { 0x39, 0x5d }, /* '9' => ']' */ + { 0x5d, 0x7e }, /* ']' => '~' */ { 0x71, 0x40 }, /* 'q' => '@' */ { 0xdc, 0x7c }, /* Pseudo-'\\' => '|' */ { 0, 0 } diff --git a/src/hci/keymap/keymap_sg.c b/src/hci/keymap/keymap_sg.c index 9a6db9cb4..2afe59c61 100644 --- a/src/hci/keymap/keymap_sg.c +++ b/src/hci/keymap/keymap_sg.c @@ -45,9 +45,11 @@ static struct keymap_key sg_basic[] = { /** "sg" AltGr remapping */ static struct keymap_key sg_altgr[] = { + { 0x27, 0x7b }, /* '\'' => '{' */ { 0x32, 0x40 }, /* '2' => '@' */ { 0x33, 0x23 }, /* '3' => '#' */ { 0x37, 0x7c }, /* '7' => '|' */ + { 0x3d, 0x7e }, /* '=' => '~' */ { 0x5c, 0x7d }, /* '\\' => '}' */ { 0, 0 } }; diff --git a/src/util/genkeymap.py b/src/util/genkeymap.py index 8df949612..632f71eda 100755 --- a/src/util/genkeymap.py +++ b/src/util/genkeymap.py @@ -33,6 +33,10 @@ import textwrap from typing import ClassVar, Optional +BACKSPACE = chr(0x7f) +"""Backspace character""" + + class KeyType(IntEnum): """Key types""" @@ -174,7 +178,7 @@ class KeyLayout(UserDict[KeyModifiers, Sequence[Key]]): KeyModifiers.NONE: [(KEY_NON_US, ord('\\'))], KeyModifiers.SHIFT: [(KEY_NON_US, ord('|'))], # Treat Ctrl-Backspace as producing Backspace rather than Ctrl-H - KeyModifiers.CTRL: [(KEY_BACKSPACE, 0x7f)], + KeyModifiers.CTRL: [(KEY_BACKSPACE, ord(BACKSPACE))], }, 'il': { # Redefine some otherwise unreachable ASCII characters @@ -200,11 +204,6 @@ class KeyLayout(UserDict[KeyModifiers, Sequence[Key]]): """Basic shifted keyboard layout""" return self[KeyModifiers.SHIFT] - @property - def altgr(self): - """AltGr keyboard layout""" - return self.get(KeyModifiers.ALTGR, self.unshifted) - @classmethod def load(cls, name: str) -> KeyLayout: """Load keymap using 'loadkeys -b'""" @@ -281,7 +280,7 @@ class BiosKeyLayout(KeyLayout): return inverse -class KeymapKeys(UserDict[str, str]): +class KeymapKeys(UserDict[str, Optional[str]]): """An ASCII character remapping""" @classmethod @@ -312,7 +311,8 @@ class KeymapKeys(UserDict[str, str]): self.ascii_name(source), self.ascii_name(target) ) for source, target in self.items() - if ord(source) & ~BiosKeyLayout.KEY_PSEUDO != ord(target) + if target + and ord(source) & ~BiosKeyLayout.KEY_PSEUDO != ord(target) ) + '\t{ 0, 0 }\n}' @@ -335,13 +335,12 @@ class Keymap: # Construct raw mapping from source ASCII to target ASCII raw = {source: self.target[key.modifiers][key.keycode].ascii for source, key in self.source.inverse.items()} - # Eliminate any null mappings, mappings that attempt to remap - # the backspace key, or identity mappings + # Eliminate any identity mappings, or mappings that attempt to + # remap the backspace key table = {source: target for source, target in raw.items() - if target - and ord(source) != 0x7f - and ord(target) != 0x7f - and source != target} + if source != target + and source != BACKSPACE + and target != BACKSPACE} # Recursively delete any mappings that would produce # unreachable alphanumerics (e.g. the "il" keymap, which maps # away the whole lower-case alphabet) @@ -354,8 +353,8 @@ class Keymap: # Sanity check: ensure that all numerics are reachable using # the same shift state digits = '1234567890' - unshifted = ''.join(table.get(x, x) for x in '1234567890') - shifted = ''.join(table.get(x, x) for x in '!@#$%^&*()') + unshifted = ''.join(table.get(x) or x for x in '1234567890') + shifted = ''.join(table.get(x) or x for x in '!@#$%^&*()') if digits not in (shifted, unshifted): raise ValueError("Inconsistent numeric remapping %s / %s" % (unshifted, shifted)) @@ -365,21 +364,22 @@ class Keymap: def altgr(self) -> KeymapKeys: """AltGr remapping table""" # Construct raw mapping from source ASCII to target ASCII - raw = {source: self.target.altgr[key.keycode].ascii - for source, key in self.source.inverse.items() - if key.modifiers == KeyModifiers.NONE} + raw = { + source: + self.target.get((key.modifiers | KeyModifiers.ALTGR), + self.target[key.modifiers])[key.keycode].ascii + for source, key in self.source.inverse.items() + } # Identify printable keys that are unreachable via the basic map basic = self.basic unmapped = set(x for x in basic.keys() if x.isascii() and x.isprintable()) remapped = set(basic.values()) unreachable = unmapped - remapped - # Eliminate any null mappings, mappings for unprintable - # characters, or mappings for characters that are reachable - # via the basic map + # Eliminate any mappings for unprintable characters, or + # mappings for characters that are reachable via the basic map table = {source: target for source, target in raw.items() if source.isprintable() - and target and target in unreachable} # Check that all characters are now reachable unreachable -= set(table.values()) -- cgit v1.2.3-55-g7522 From 5d22307c4161dde453d50e8dc7bef8b3a2f6c9b3 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 15 Feb 2022 14:28:01 +0000 Subject: [image] Do not clear current working URI when executing embedded image Embedded images do not have an associated URI. This currently causes the current working URI (cwuri) to be cleared when starting an embedded image. If the current working URI has been set via a ${next-server} setting from a cached DHCP packet then this will result in unexpected behaviour. An attempt by the embedded script to use a relative URI to download files from the TFTP server will fail with the error: Could not start download: Operation not supported (ipxe.org/3c092083) Rerunning the "dhcp" command will not fix this error, since the TFTP settings applicator will not see any change to the ${next-server} setting and so will not reset the current working URI. Fix by setting the current working URI to the image's URI only if the image actually has an associated URI. Debugged-by: Ignat Korchagin Originally-fixed-by: Ignat Korchagin Tested-by: Ignat Korchagin Signed-off-by: Michael Brown --- src/core/image.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/image.c b/src/core/image.c index ce8cf868b..3e236ca60 100644 --- a/src/core/image.c +++ b/src/core/image.c @@ -338,9 +338,12 @@ int image_exec ( struct image *image ) { /* Sanity check */ assert ( image->flags & IMAGE_REGISTERED ); - /* Switch current working directory to be that of the image itself */ + /* Switch current working directory to be that of the image + * itself, if applicable + */ old_cwuri = uri_get ( cwuri ); - churi ( image->uri ); + if ( image->uri ) + churi ( image->uri ); /* Preserve record of any currently-running image */ saved_current_image = current_image; -- cgit v1.2.3-55-g7522 From 04288974f6d81019314cbf9cbd72ab1fae95496f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 16 Feb 2022 12:30:02 +0000 Subject: [pci] Ensure that pci_read_config() initialises all fields As per the general pattern for initialisation functions in iPXE, pci_init() saves code size by assuming that the caller has already zeroed the underlying storage (e.g. as part of zeroing a larger containing structure). There are several places within the code where pci_init() is deliberately used to initialise a transient struct pci_device without zeroing the entire structure, because the calling code knows that only the PCI bus:dev.fn address is required to be initialised (e.g. when reading from PCI configuration space). Ensure that using pci_init() followed by pci_read_config() will fully initialise the struct pci_device even if the caller did not previously zero the underlying storage, since Coverity reports that there are several places in the code that rely upon this. Signed-off-by: Michael Brown --- src/drivers/bus/pci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c index 1b7350c8b..5891e42ff 100644 --- a/src/drivers/bus/pci.c +++ b/src/drivers/bus/pci.c @@ -121,6 +121,11 @@ static void pci_read_bases ( struct pci_device *pci ) { unsigned long bar; int reg; + /* Clear any existing base addresses */ + pci->ioaddr = 0; + pci->membase = 0; + + /* Get first memory and I/O BAR addresses */ for ( reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4 ) { bar = pci_bar ( pci, reg ); if ( bar & PCI_BASE_ADDRESS_SPACE_IO ) { -- cgit v1.2.3-55-g7522 From 252cff5e9a0b67a27e1837cb674491692b717fd0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 16 Feb 2022 13:22:56 +0000 Subject: [xsigo] Avoid storing unused uninitialised fields in gateway address As reported by Coverity, xsmp_rx_xve_modify() currently passes a partially initialised struct ib_address_vector to xve_update_tca() and thence to eoib_set_gateway(), which uses memcpy() to store the whole structure including the (unused and unneeded) uninitialised fields. Silence the Coverity warning by zeroing the whole structure. Signed-off-by: Michael Brown --- src/net/infiniband/xsigo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/net/infiniband/xsigo.c b/src/net/infiniband/xsigo.c index 0ee753c38..4f5c618d7 100644 --- a/src/net/infiniband/xsigo.c +++ b/src/net/infiniband/xsigo.c @@ -870,6 +870,7 @@ static int xsmp_rx_xve_modify ( struct xsigo_manager *xcm, * erroneously transmitted as little-endian. */ mtu = ntohs ( msg->mtu ); + memset ( &tca, 0, sizeof ( tca ) ); tca.qpn = ntohl ( msg->tca.data ); tca.qkey = ntohs ( msg->tca.qkey ); tca.gid_present = 1; -- cgit v1.2.3-55-g7522 From 11e17991d0729fd17ab06d94ec67a8ca48032af4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 16 Feb 2022 00:11:33 +0000 Subject: [console] Ensure that US keyboard map appears at start of linker table Signed-off-by: Michael Brown --- src/hci/keymap/keymap_us.c | 2 +- src/include/ipxe/keymap.h | 5 ++++- src/util/genkeymap.py | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/hci/keymap/keymap_us.c b/src/hci/keymap/keymap_us.c index 6432474e2..b8e604a48 100644 --- a/src/hci/keymap/keymap_us.c +++ b/src/hci/keymap/keymap_us.c @@ -21,7 +21,7 @@ static struct keymap_key us_altgr[] = { }; /** "us" keyboard map */ -struct keymap us_keymap __keymap = { +struct keymap us_keymap __keymap_default = { .name = "us", .basic = us_basic, .altgr = us_altgr, diff --git a/src/include/ipxe/keymap.h b/src/include/ipxe/keymap.h index 72b6961ef..392d3ab8f 100644 --- a/src/include/ipxe/keymap.h +++ b/src/include/ipxe/keymap.h @@ -39,8 +39,11 @@ struct keymap { /** Keyboard mapping table */ #define KEYMAP __table ( struct keymap, "keymap" ) +/** Define a default keyboard mapping */ +#define __keymap_default __table_entry ( KEYMAP, 01 ) + /** Define a keyboard mapping */ -#define __keymap __table_entry ( KEYMAP, 01 ) +#define __keymap __table_entry ( KEYMAP, 02 ) /** Mappable character mask */ #define KEYMAP_MASK 0xff diff --git a/src/util/genkeymap.py b/src/util/genkeymap.py index 632f71eda..9fd987477 100755 --- a/src/util/genkeymap.py +++ b/src/util/genkeymap.py @@ -399,6 +399,7 @@ class Keymap: keymap_name = self.cname("keymap") basic_name = self.cname("basic") altgr_name = self.cname("altgr") + attribute = "__keymap_default" if self.name == "us" else "__keymap" code = textwrap.dedent(f""" /** @file * @@ -419,7 +420,7 @@ class Keymap: static struct keymap_key {altgr_name}[] = %s; /** "{self.name}" keyboard map */ - struct keymap {keymap_name} __keymap = {{ + struct keymap {keymap_name} {attribute} = {{ \t.name = "{self.name}", \t.basic = {basic_name}, \t.altgr = {altgr_name}, -- cgit v1.2.3-55-g7522 From 674963e2a63c2b16b60db815b6017b1c3f3e86c2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 16 Feb 2022 00:12:55 +0000 Subject: [settings] Always process all settings applicators Settings applicators are entirely independent, and there is no reason why a failure in one applicator should prevent other applicators from being processed. Signed-off-by: Michael Brown --- src/core/settings.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/core/settings.c b/src/core/settings.c index fcdf98d2b..da075baa8 100644 --- a/src/core/settings.c +++ b/src/core/settings.c @@ -411,9 +411,8 @@ struct settings * find_settings ( const char *name ) { /** * Apply all settings * - * @ret rc Return status code */ -static int apply_settings ( void ) { +static void apply_settings ( void ) { struct settings_applicator *applicator; int rc; @@ -422,11 +421,9 @@ static int apply_settings ( void ) { if ( ( rc = applicator->apply() ) != 0 ) { DBG ( "Could not apply settings using applicator " "%p: %s\n", applicator, strerror ( rc ) ); - return rc; + /* Continue to apply remaining settings */ } } - - return 0; } /** @@ -644,8 +641,7 @@ int store_setting ( struct settings *settings, const struct setting *setting, */ for ( ; settings ; settings = settings->parent ) { if ( settings == &settings_root ) { - if ( ( rc = apply_settings() ) != 0 ) - return rc; + apply_settings(); break; } } -- cgit v1.2.3-55-g7522 From 304333dace992ea4b876a074c42bb7fd752137ca Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 16 Feb 2022 00:14:38 +0000 Subject: [console] Support changing keyboard map at runtime Provide the special keyboard map named "dynamic" which allows the active keyboard map to be selected at runtime via the ${keymap} setting, e.g.: #define KEYBOARD_MAP dynamic iPXE> set keymap uk Signed-off-by: Michael Brown --- src/core/dynkeymap.c | 131 +++++++++++++++++++++++++++++++++++++++++++++ src/core/keymap.c | 42 ++++++++++++++- src/include/ipxe/errfile.h | 1 + src/include/ipxe/keymap.h | 2 + 4 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 src/core/dynkeymap.c diff --git a/src/core/dynkeymap.c b/src/core/dynkeymap.c new file mode 100644 index 000000000..2f7c49937 --- /dev/null +++ b/src/core/dynkeymap.c @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2022 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Dynamic keyboard mappings + * + */ + +#include +#include +#include +#include + +/** + * Require a keyboard map + * + * @v name Keyboard map name + */ +#define REQUIRE_KEYMAP( name ) REQUIRE_OBJECT ( keymap_ ## name ) + +/** Keyboard map setting */ +const struct setting keymap_setting __setting ( SETTING_MISC, keymap ) = { + .name = "keymap", + .description = "Keyboard map", + .type = &setting_type_string, +}; + +/** + * Apply keyboard map settings + * + * @ret rc Return status code + */ +static int keymap_apply ( void ) { + struct keymap *keymap; + char *name; + int rc; + + /* Fetch keyboard map name */ + fetch_string_setting_copy ( NULL, &keymap_setting, &name ); + + /* Identify keyboard map */ + if ( name ) { + /* Identify named keyboard map */ + keymap = keymap_find ( name ); + if ( ! keymap ) { + DBGC ( &keymap_setting, "KEYMAP could not identify " + "\"%s\"\n", name ); + rc = -ENOENT; + goto err_unknown; + } + } else { + /* Use default keyboard map */ + keymap = NULL; + } + + /* Set keyboard map */ + keymap_set ( keymap ); + + /* Success */ + rc = 0; + + err_unknown: + free ( name ); + return rc; +} + +/** Keyboard map setting applicator */ +struct settings_applicator keymap_applicator __settings_applicator = { + .apply = keymap_apply, +}; + +/* Provide virtual "dynamic" keyboard map for linker */ +PROVIDE_SYMBOL ( obj_keymap_dynamic ); + +/* Drag in keyboard maps via keymap_setting */ +REQUIRING_SYMBOL ( keymap_setting ); + +/* Require all known keyboard maps */ +REQUIRE_KEYMAP ( al ); +REQUIRE_KEYMAP ( by ); +REQUIRE_KEYMAP ( cf ); +REQUIRE_KEYMAP ( cz ); +REQUIRE_KEYMAP ( de ); +REQUIRE_KEYMAP ( dk ); +REQUIRE_KEYMAP ( es ); +REQUIRE_KEYMAP ( et ); +REQUIRE_KEYMAP ( fi ); +REQUIRE_KEYMAP ( fr ); +REQUIRE_KEYMAP ( gr ); +REQUIRE_KEYMAP ( hu ); +REQUIRE_KEYMAP ( il ); +REQUIRE_KEYMAP ( it ); +REQUIRE_KEYMAP ( lt ); +REQUIRE_KEYMAP ( mk ); +REQUIRE_KEYMAP ( mt ); +REQUIRE_KEYMAP ( nl ); +REQUIRE_KEYMAP ( no ); +REQUIRE_KEYMAP ( no_latin1 ); +REQUIRE_KEYMAP ( pl ); +REQUIRE_KEYMAP ( pt ); +REQUIRE_KEYMAP ( ro ); +REQUIRE_KEYMAP ( ru ); +REQUIRE_KEYMAP ( se ); +REQUIRE_KEYMAP ( sg ); +REQUIRE_KEYMAP ( sr_latin ); +REQUIRE_KEYMAP ( ua ); +REQUIRE_KEYMAP ( uk ); +REQUIRE_KEYMAP ( us ); diff --git a/src/core/keymap.c b/src/core/keymap.c index 3fa85f74e..36db7bd4c 100644 --- a/src/core/keymap.c +++ b/src/core/keymap.c @@ -23,6 +23,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include #include #include #include @@ -49,7 +50,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); static TABLE_START ( keymap_start, KEYMAP ); /** Current keyboard mapping */ -static struct keymap *keymap = keymap_start; +static struct keymap *keymap_current = keymap_start; /** * Remap a key @@ -58,6 +59,7 @@ static struct keymap *keymap = keymap_start; * @ret mapped Mapped character */ unsigned int key_remap ( unsigned int character ) { + struct keymap *keymap = keymap_current; unsigned int mapped = ( character & KEYMAP_MASK ); struct keymap_key *key; @@ -88,6 +90,42 @@ unsigned int key_remap ( unsigned int character ) { /* Clear flags */ mapped &= ASCII_MASK; - DBGC2 ( &keymap, "KEYMAP mapped %04x => %02x\n", character, mapped ); + DBGC2 ( &keymap_current, "KEYMAP mapped %04x => %02x\n", + character, mapped ); return mapped; } + +/** + * Find keyboard map by name + * + * @v name Keyboard map name + * @ret keymap Keyboard map, or NULL if not found + */ +struct keymap * keymap_find ( const char *name ) { + struct keymap *keymap; + + /* Find matching keyboard map */ + for_each_table_entry ( keymap, KEYMAP ) { + if ( strcmp ( keymap->name, name ) == 0 ) + return keymap; + } + + return NULL; +} + +/** + * Set keyboard map + * + * @v keymap Keyboard map, or NULL to use default + */ +void keymap_set ( struct keymap *keymap ) { + + /* Use default keymap if none specified */ + if ( ! keymap ) + keymap = keymap_start; + + /* Set new keyboard map */ + if ( keymap != keymap_current ) + DBGC ( &keymap_current, "KEYMAP using \"%s\"\n", keymap->name ); + keymap_current = keymap; +} diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 23e406b62..81f555725 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -395,6 +395,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_efi_cachedhcp ( ERRFILE_OTHER | 0x00550000 ) #define ERRFILE_linux_sysfs ( ERRFILE_OTHER | 0x00560000 ) #define ERRFILE_linux_acpi ( ERRFILE_OTHER | 0x00570000 ) +#define ERRFILE_dynkeymap ( ERRFILE_OTHER | 0x00580000 ) /** @} */ diff --git a/src/include/ipxe/keymap.h b/src/include/ipxe/keymap.h index 392d3ab8f..8bfbe07a5 100644 --- a/src/include/ipxe/keymap.h +++ b/src/include/ipxe/keymap.h @@ -73,5 +73,7 @@ struct keymap { #define KEYMAP_ALTGR 0x0800 extern unsigned int key_remap ( unsigned int character ); +extern struct keymap * keymap_find ( const char *name ); +extern void keymap_set ( struct keymap *keymap ); #endif /* _IPXE_KEYMAP_H */ -- cgit v1.2.3-55-g7522 From bc5c612f756b11c08e5e1eaeee3c1fd91252534d Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 16 Feb 2022 15:19:35 +0000 Subject: [console] Include mappings for AltGr-Shift- The BIOS console's interpretation of LShift+RShift as equivalent to AltGr requires the shifted ASCII characters to be present in the AltGr mapping table, to allow AltGr-Shift- to be interpreted in the same way as AltGr-. For keyboard layouts that have different ASCII characters for AltGr- and AltGr-Shift-, this will potentially leave the character for AltGr- inaccessible via the BIOS console if the BIOS requires the use of the LShift+RShift workaround. This theoretically affects the numeric keys in the Lithuanian ("lt") keyboard layout (where the numerals are accessed via AltGr- and punctuation characters via AltGr-Shift-), but the simple workaround for that keyboard layout is to avoid using AltGr and Shift entirely since the unmodified numeric keys are not remapped anyway. Signed-off-by: Michael Brown --- src/hci/keymap/keymap_al.c | 1 + src/hci/keymap/keymap_cf.c | 6 ++++++ src/hci/keymap/keymap_cz.c | 9 +++++++++ src/hci/keymap/keymap_de.c | 8 ++++++++ src/hci/keymap/keymap_dk.c | 8 ++++++++ src/hci/keymap/keymap_es.c | 8 ++++++++ src/hci/keymap/keymap_fi.c | 4 ++++ src/hci/keymap/keymap_fr.c | 8 ++++++++ src/hci/keymap/keymap_hu.c | 13 +++++++++++++ src/hci/keymap/keymap_il.c | 5 +++++ src/hci/keymap/keymap_it.c | 8 ++++++++ src/hci/keymap/keymap_lt.c | 1 + src/hci/keymap/keymap_mt.c | 5 +++++ src/hci/keymap/keymap_nl.c | 1 + src/hci/keymap/keymap_no-latin1.c | 4 ++++ src/hci/keymap/keymap_no.c | 6 ++++++ src/hci/keymap/keymap_pt.c | 5 +++++ src/hci/keymap/keymap_se.c | 6 ++++++ src/hci/keymap/keymap_sg.c | 7 +++++++ src/util/genkeymap.py | 8 +++++--- 20 files changed, 118 insertions(+), 3 deletions(-) diff --git a/src/hci/keymap/keymap_al.c b/src/hci/keymap/keymap_al.c index 6e14395a5..ad4792b9b 100644 --- a/src/hci/keymap/keymap_al.c +++ b/src/hci/keymap/keymap_al.c @@ -39,6 +39,7 @@ static struct keymap_key al_basic[] = { static struct keymap_key al_altgr[] = { { 0x21, 0x7e }, /* '!' => '~' */ { 0x26, 0x60 }, /* '&' => '`' */ + { 0x29, 0x7e }, /* ')' => '~' */ { 0x30, 0x7e }, /* '0' => '~' */ { 0x31, 0x7e }, /* '1' => '~' */ { 0x34, 0x7e }, /* '4' => '~' */ diff --git a/src/hci/keymap/keymap_cf.c b/src/hci/keymap/keymap_cf.c index 55bbfc1a4..7ecfc4448 100644 --- a/src/hci/keymap/keymap_cf.c +++ b/src/hci/keymap/keymap_cf.c @@ -30,11 +30,17 @@ static struct keymap_key cf_basic[] = { /** "cf" AltGr remapping */ static struct keymap_key cf_altgr[] = { + { 0x22, 0x7b }, /* '"' => '{' */ { 0x27, 0x7b }, /* '\'' => '{' */ { 0x32, 0x40 }, /* '2' => '@' */ + { 0x3a, 0x7e }, /* ':' => '~' */ { 0x3b, 0x7e }, /* ';' => '~' */ { 0x5c, 0x7d }, /* '\\' => '}' */ { 0x60, 0x5c }, /* '`' => '\\' */ + { 0x7b, 0x5b }, /* '{' => '[' */ + { 0x7c, 0x7d }, /* '|' => '}' */ + { 0x7d, 0x5d }, /* '}' => ']' */ + { 0x7e, 0x5c }, /* '~' => '\\' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_cz.c b/src/hci/keymap/keymap_cz.c index 08e9638e1..dd793a8d0 100644 --- a/src/hci/keymap/keymap_cz.c +++ b/src/hci/keymap/keymap_cz.c @@ -52,6 +52,7 @@ static struct keymap_key cz_basic[] = { static struct keymap_key cz_altgr[] = { { 0x21, 0x7e }, /* '!' => '~' */ { 0x24, 0x7e }, /* '$' => '~' */ + { 0x28, 0x7b }, /* '(' => '{' */ { 0x29, 0x7e }, /* ')' => '~' */ { 0x2c, 0x3c }, /* ',' => '<' */ { 0x2e, 0x3e }, /* '.' => '>' */ @@ -65,7 +66,13 @@ static struct keymap_key cz_altgr[] = { { 0x39, 0x7b }, /* '9' => '{' */ { 0x3a, 0x7e }, /* ':' => '~' */ { 0x3b, 0x24 }, /* ';' => '$' */ + { 0x41, 0x7e }, /* 'A' => '~' */ + { 0x42, 0x7b }, /* 'B' => '{' */ + { 0x43, 0x26 }, /* 'C' => '&' */ + { 0x46, 0x5b }, /* 'F' => '[' */ + { 0x47, 0x5d }, /* 'G' => ']' */ { 0x4b, 0x26 }, /* 'K' => '&' */ + { 0x56, 0x40 }, /* 'V' => '@' */ { 0x58, 0x3e }, /* 'X' => '>' */ { 0x5a, 0x3c }, /* 'Z' => '<' */ { 0x61, 0x7e }, /* 'a' => '~' */ @@ -76,6 +83,8 @@ static struct keymap_key cz_altgr[] = { { 0x6e, 0x7d }, /* 'n' => '}' */ { 0x76, 0x40 }, /* 'v' => '@' */ { 0x78, 0x23 }, /* 'x' => '#' */ + { 0x7b, 0x5b }, /* '{' => '[' */ + { 0x7d, 0x5d }, /* '}' => ']' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_de.c b/src/hci/keymap/keymap_de.c index ca5e4a9a9..fb1136dc7 100644 --- a/src/hci/keymap/keymap_de.c +++ b/src/hci/keymap/keymap_de.c @@ -45,15 +45,23 @@ static struct keymap_key de_basic[] = { /** "de" AltGr remapping */ static struct keymap_key de_altgr[] = { + { 0x26, 0x7b }, /* '&' => '{' */ + { 0x28, 0x5d }, /* '(' => ']' */ + { 0x29, 0x7d }, /* ')' => '}' */ + { 0x2a, 0x5b }, /* '*' => '[' */ { 0x2d, 0x5c }, /* '-' => '\\' */ { 0x30, 0x7d }, /* '0' => '}' */ { 0x37, 0x7b }, /* '7' => '{' */ { 0x38, 0x5b }, /* '8' => '[' */ { 0x39, 0x5d }, /* '9' => ']' */ + { 0x3a, 0x7e }, /* ':' => '~' */ { 0x3b, 0x7e }, /* ';' => '~' */ + { 0x51, 0x40 }, /* 'Q' => '@' */ { 0x5d, 0x7e }, /* ']' => '~' */ + { 0x5f, 0x5c }, /* '_' => '\\' */ { 0x71, 0x40 }, /* 'q' => '@' */ { 0x7c, 0x7e }, /* '|' => '~' */ + { 0x7d, 0x7e }, /* '}' => '~' */ { 0xdc, 0x7c }, /* Pseudo-'\\' => '|' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_dk.c b/src/hci/keymap/keymap_dk.c index 5b229024a..45cdeb25d 100644 --- a/src/hci/keymap/keymap_dk.c +++ b/src/hci/keymap/keymap_dk.c @@ -37,6 +37,11 @@ static struct keymap_key dk_basic[] = { /** "dk" AltGr remapping */ static struct keymap_key dk_altgr[] = { + { 0x26, 0x7b }, /* '&' => '{' */ + { 0x28, 0x5d }, /* '(' => ']' */ + { 0x29, 0x7d }, /* ')' => '}' */ + { 0x2a, 0x5b }, /* '*' => '[' */ + { 0x2b, 0x7c }, /* '+' => '|' */ { 0x30, 0x7d }, /* '0' => '}' */ { 0x32, 0x40 }, /* '2' => '@' */ { 0x34, 0x24 }, /* '4' => '$' */ @@ -45,9 +50,12 @@ static struct keymap_key dk_altgr[] = { { 0x39, 0x5d }, /* '9' => ']' */ { 0x3a, 0x7e }, /* ':' => '~' */ { 0x3d, 0x7c }, /* '=' => '|' */ + { 0x51, 0x40 }, /* 'Q' => '@' */ { 0x5c, 0x7e }, /* '\\' => '~' */ { 0x5d, 0x7e }, /* ']' => '~' */ { 0x71, 0x40 }, /* 'q' => '@' */ + { 0x7c, 0x7e }, /* '|' => '~' */ + { 0xfc, 0x5c }, /* Pseudo-'|' => '\\' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_es.c b/src/hci/keymap/keymap_es.c index 93d87f2a0..c1c55b348 100644 --- a/src/hci/keymap/keymap_es.c +++ b/src/hci/keymap/keymap_es.c @@ -37,8 +37,13 @@ static struct keymap_key es_basic[] = { /** "es" AltGr remapping */ static struct keymap_key es_altgr[] = { + { 0x21, 0x7c }, /* '!' => '|' */ { 0x22, 0x7b }, /* '"' => '{' */ + { 0x26, 0x7b }, /* '&' => '{' */ { 0x27, 0x7b }, /* '\'' => '{' */ + { 0x28, 0x5d }, /* '(' => ']' */ + { 0x29, 0x7d }, /* ')' => '}' */ + { 0x2a, 0x5b }, /* '*' => '[' */ { 0x2b, 0x7e }, /* '+' => '~' */ { 0x2d, 0x5c }, /* '-' => '\\' */ { 0x30, 0x7d }, /* '0' => '}' */ @@ -52,9 +57,12 @@ static struct keymap_key es_altgr[] = { { 0x3a, 0x7e }, /* ':' => '~' */ { 0x3b, 0x7e }, /* ';' => '~' */ { 0x3d, 0x7e }, /* '=' => '~' */ + { 0x51, 0x40 }, /* 'Q' => '@' */ { 0x5c, 0x7d }, /* '\\' => '}' */ + { 0x5f, 0x5c }, /* '_' => '\\' */ { 0x60, 0x5c }, /* '`' => '\\' */ { 0x71, 0x40 }, /* 'q' => '@' */ + { 0x7b, 0x5b }, /* '{' => '[' */ { 0x7c, 0x7e }, /* '|' => '~' */ { 0x7e, 0x5c }, /* '~' => '\\' */ { 0xdc, 0x7c }, /* Pseudo-'\\' => '|' */ diff --git a/src/hci/keymap/keymap_fi.c b/src/hci/keymap/keymap_fi.c index ac69813e8..8bcd0c393 100644 --- a/src/hci/keymap/keymap_fi.c +++ b/src/hci/keymap/keymap_fi.c @@ -35,6 +35,8 @@ static struct keymap_key fi_basic[] = { /** "fi" AltGr remapping */ static struct keymap_key fi_altgr[] = { + { 0x26, 0x7b }, /* '&' => '{' */ + { 0x29, 0x7d }, /* ')' => '}' */ { 0x2d, 0x5c }, /* '-' => '\\' */ { 0x30, 0x7d }, /* '0' => '}' */ { 0x32, 0x40 }, /* '2' => '@' */ @@ -44,6 +46,8 @@ static struct keymap_key fi_altgr[] = { { 0x39, 0x5d }, /* '9' => ']' */ { 0x49, 0x7c }, /* 'I' => '|' */ { 0x5d, 0x7e }, /* ']' => '~' */ + { 0x5f, 0x5c }, /* '_' => '\\' */ + { 0x7d, 0x7e }, /* '}' => '~' */ { 0xdc, 0x7c }, /* Pseudo-'\\' => '|' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_fr.c b/src/hci/keymap/keymap_fr.c index ad797f876..23a0837fb 100644 --- a/src/hci/keymap/keymap_fr.c +++ b/src/hci/keymap/keymap_fr.c @@ -65,6 +65,11 @@ static struct keymap_key fr_basic[] = { /** "fr" AltGr remapping */ static struct keymap_key fr_altgr[] = { + { 0x25, 0x5b }, /* '%' => '[' */ + { 0x26, 0x60 }, /* '&' => '`' */ + { 0x29, 0x40 }, /* ')' => '@' */ + { 0x2a, 0x5c }, /* '*' => '\\' */ + { 0x2b, 0x7d }, /* '+' => '}' */ { 0x2d, 0x5d }, /* '-' => ']' */ { 0x30, 0x40 }, /* '0' => '@' */ { 0x33, 0x23 }, /* '3' => '#' */ @@ -74,7 +79,10 @@ static struct keymap_key fr_altgr[] = { { 0x37, 0x60 }, /* '7' => '`' */ { 0x38, 0x5c }, /* '8' => '\\' */ { 0x3d, 0x7d }, /* '=' => '}' */ + { 0x41, 0x40 }, /* 'A' => '@' */ { 0x5c, 0x60 }, /* '\\' => '`' */ + { 0x5e, 0x7c }, /* '^' => '|' */ + { 0x5f, 0x5d }, /* '_' => ']' */ { 0x61, 0x40 }, /* 'a' => '@' */ { 0xdc, 0x7c }, /* Pseudo-'\\' => '|' */ { 0, 0 } diff --git a/src/hci/keymap/keymap_hu.c b/src/hci/keymap/keymap_hu.c index d64baaebf..771671408 100644 --- a/src/hci/keymap/keymap_hu.c +++ b/src/hci/keymap/keymap_hu.c @@ -39,7 +39,9 @@ static struct keymap_key hu_basic[] = { static struct keymap_key hu_altgr[] = { { 0x21, 0x7e }, /* '!' => '~' */ { 0x23, 0x5e }, /* '#' => '^' */ + { 0x24, 0x7e }, /* '$' => '~' */ { 0x26, 0x60 }, /* '&' => '`' */ + { 0x29, 0x7e }, /* ')' => '~' */ { 0x2c, 0x3b }, /* ',' => ';' */ { 0x2e, 0x3e }, /* '.' => '>' */ { 0x2f, 0x2a }, /* '/' => '*' */ @@ -49,8 +51,19 @@ static struct keymap_key hu_altgr[] = { { 0x33, 0x5e }, /* '3' => '^' */ { 0x34, 0x7e }, /* '4' => '~' */ { 0x37, 0x60 }, /* '7' => '`' */ + { 0x3a, 0x24 }, /* ':' => '$' */ { 0x3b, 0x24 }, /* ';' => '$' */ + { 0x3c, 0x3b }, /* '<' => ';' */ + { 0x40, 0x5e }, /* '@' => '^' */ + { 0x42, 0x7b }, /* 'B' => '{' */ + { 0x43, 0x26 }, /* 'C' => '&' */ + { 0x46, 0x5b }, /* 'F' => '[' */ + { 0x47, 0x5d }, /* 'G' => ']' */ { 0x4b, 0x26 }, /* 'K' => '&' */ + { 0x4d, 0x3c }, /* 'M' => '<' */ + { 0x51, 0x5c }, /* 'Q' => '\\' */ + { 0x56, 0x40 }, /* 'V' => '@' */ + { 0x57, 0x7c }, /* 'W' => '|' */ { 0x58, 0x3e }, /* 'X' => '>' */ { 0x5a, 0x3c }, /* 'Z' => '<' */ { 0x62, 0x7b }, /* 'b' => '{' */ diff --git a/src/hci/keymap/keymap_il.c b/src/hci/keymap/keymap_il.c index 95e33a0bd..b11e7ce71 100644 --- a/src/hci/keymap/keymap_il.c +++ b/src/hci/keymap/keymap_il.c @@ -31,7 +31,12 @@ static struct keymap_key il_basic[] = { /** "il" AltGr remapping */ static struct keymap_key il_altgr[] = { + { 0x22, 0x27 }, /* '"' => '\'' */ + { 0x3f, 0x2f }, /* '?' => '/' */ { 0x5c, 0x60 }, /* '\\' => '`' */ + { 0x71, 0x2f }, /* 'q' => '/' */ + { 0x77, 0x27 }, /* 'w' => '\'' */ + { 0x7c, 0x60 }, /* '|' => '`' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_it.c b/src/hci/keymap/keymap_it.c index 2511bb791..bb14ae1bd 100644 --- a/src/hci/keymap/keymap_it.c +++ b/src/hci/keymap/keymap_it.c @@ -37,17 +37,25 @@ static struct keymap_key it_basic[] = { /** "it" AltGr remapping */ static struct keymap_key it_altgr[] = { + { 0x22, 0x23 }, /* '"' => '#' */ { 0x23, 0x7e }, /* '#' => '~' */ + { 0x26, 0x7b }, /* '&' => '{' */ { 0x27, 0x23 }, /* '\'' => '#' */ + { 0x28, 0x5d }, /* '(' => ']' */ + { 0x29, 0x7d }, /* ')' => '}' */ + { 0x2a, 0x5b }, /* '*' => '[' */ { 0x2d, 0x60 }, /* '-' => '`' */ { 0x30, 0x7d }, /* '0' => '}' */ { 0x37, 0x7b }, /* '7' => '{' */ { 0x38, 0x5b }, /* '8' => '[' */ { 0x39, 0x5d }, /* '9' => ']' */ + { 0x3a, 0x40 }, /* ':' => '@' */ { 0x3b, 0x40 }, /* ';' => '@' */ { 0x3d, 0x7e }, /* '=' => '~' */ { 0x40, 0x7e }, /* '@' => '~' */ + { 0x51, 0x40 }, /* 'Q' => '@' */ { 0x5c, 0x60 }, /* '\\' => '`' */ + { 0x5f, 0x60 }, /* '_' => '`' */ { 0x71, 0x40 }, /* 'q' => '@' */ { 0x7c, 0x7e }, /* '|' => '~' */ { 0, 0 } diff --git a/src/hci/keymap/keymap_lt.c b/src/hci/keymap/keymap_lt.c index a237b1d62..f8e60a5c5 100644 --- a/src/hci/keymap/keymap_lt.c +++ b/src/hci/keymap/keymap_lt.c @@ -20,6 +20,7 @@ static struct keymap_key lt_altgr[] = { { 0x22, 0x5e }, /* '"' => '^' */ { 0x27, 0x5e }, /* '\'' => '^' */ { 0x4b, 0x26 }, /* 'K' => '&' */ + { 0x51, 0x40 }, /* 'Q' => '@' */ { 0x71, 0x40 }, /* 'q' => '@' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_mt.c b/src/hci/keymap/keymap_mt.c index a95c5b4e8..0997bfe93 100644 --- a/src/hci/keymap/keymap_mt.c +++ b/src/hci/keymap/keymap_mt.c @@ -22,11 +22,16 @@ static struct keymap_key mt_basic[] = { /** "mt" AltGr remapping */ static struct keymap_key mt_altgr[] = { + { 0x26, 0x7b }, /* '&' => '{' */ + { 0x28, 0x5d }, /* '(' => ']' */ + { 0x29, 0x7d }, /* ')' => '}' */ + { 0x2a, 0x5b }, /* '*' => '[' */ { 0x30, 0x7d }, /* '0' => '}' */ { 0x37, 0x7b }, /* '7' => '{' */ { 0x38, 0x5b }, /* '8' => '[' */ { 0x39, 0x5d }, /* '9' => ']' */ { 0x5c, 0x60 }, /* '\\' => '`' */ + { 0x7e, 0x60 }, /* '~' => '`' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_nl.c b/src/hci/keymap/keymap_nl.c index 8038d4d37..97c7e8b5d 100644 --- a/src/hci/keymap/keymap_nl.c +++ b/src/hci/keymap/keymap_nl.c @@ -46,6 +46,7 @@ static struct keymap_key nl_altgr[] = { { 0x2d, 0x5c }, /* '-' => '\\' */ { 0x38, 0x7b }, /* '8' => '{' */ { 0x39, 0x7d }, /* '9' => '}' */ + { 0x5f, 0x5c }, /* '_' => '\\' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_no-latin1.c b/src/hci/keymap/keymap_no-latin1.c index 1f4cc38d3..80df84889 100644 --- a/src/hci/keymap/keymap_no-latin1.c +++ b/src/hci/keymap/keymap_no-latin1.c @@ -40,7 +40,11 @@ static struct keymap_key no_latin1_basic[] = { /** "no-latin1" AltGr remapping */ static struct keymap_key no_latin1_altgr[] = { { 0x22, 0x5b }, /* '"' => '[' */ + { 0x26, 0x7b }, /* '&' => '{' */ { 0x27, 0x7b }, /* '\'' => '{' */ + { 0x28, 0x5d }, /* '(' => ']' */ + { 0x29, 0x7d }, /* ')' => '}' */ + { 0x2a, 0x5b }, /* '*' => '[' */ { 0x30, 0x7d }, /* '0' => '}' */ { 0x32, 0x40 }, /* '2' => '@' */ { 0x37, 0x7b }, /* '7' => '{' */ diff --git a/src/hci/keymap/keymap_no.c b/src/hci/keymap/keymap_no.c index 239e3aea5..0a624c3ab 100644 --- a/src/hci/keymap/keymap_no.c +++ b/src/hci/keymap/keymap_no.c @@ -39,6 +39,10 @@ static struct keymap_key no_basic[] = { /** "no" AltGr remapping */ static struct keymap_key no_altgr[] = { + { 0x26, 0x7b }, /* '&' => '{' */ + { 0x28, 0x5d }, /* '(' => ']' */ + { 0x29, 0x7d }, /* ')' => '}' */ + { 0x2a, 0x5b }, /* '*' => '[' */ { 0x30, 0x7d }, /* '0' => '}' */ { 0x32, 0x40 }, /* '2' => '@' */ { 0x34, 0x24 }, /* '4' => '$' */ @@ -46,9 +50,11 @@ static struct keymap_key no_altgr[] = { { 0x38, 0x5b }, /* '8' => '[' */ { 0x39, 0x5d }, /* '9' => ']' */ { 0x3a, 0x7e }, /* ':' => '~' */ + { 0x51, 0x40 }, /* 'Q' => '@' */ { 0x5c, 0x7e }, /* '\\' => '~' */ { 0x5d, 0x7e }, /* ']' => '~' */ { 0x71, 0x40 }, /* 'q' => '@' */ + { 0x7c, 0x7e }, /* '|' => '~' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_pt.c b/src/hci/keymap/keymap_pt.c index 75bf565ce..3133c1561 100644 --- a/src/hci/keymap/keymap_pt.c +++ b/src/hci/keymap/keymap_pt.c @@ -40,11 +40,16 @@ static struct keymap_key pt_basic[] = { /** "pt" AltGr remapping */ static struct keymap_key pt_altgr[] = { + { 0x26, 0x7b }, /* '&' => '{' */ + { 0x28, 0x5d }, /* '(' => ']' */ + { 0x29, 0x7d }, /* ')' => '}' */ + { 0x2a, 0x5b }, /* '*' => '[' */ { 0x30, 0x7d }, /* '0' => '}' */ { 0x32, 0x40 }, /* '2' => '@' */ { 0x37, 0x7b }, /* '7' => '{' */ { 0x38, 0x5b }, /* '8' => '[' */ { 0x39, 0x5d }, /* '9' => ']' */ + { 0x51, 0x40 }, /* 'Q' => '@' */ { 0x71, 0x40 }, /* 'q' => '@' */ { 0, 0 } }; diff --git a/src/hci/keymap/keymap_se.c b/src/hci/keymap/keymap_se.c index 9c6cf64a4..2bac96985 100644 --- a/src/hci/keymap/keymap_se.c +++ b/src/hci/keymap/keymap_se.c @@ -37,6 +37,10 @@ static struct keymap_key se_basic[] = { /** "se" AltGr remapping */ static struct keymap_key se_altgr[] = { + { 0x26, 0x7b }, /* '&' => '{' */ + { 0x28, 0x5d }, /* '(' => ']' */ + { 0x29, 0x7d }, /* ')' => '}' */ + { 0x2a, 0x5b }, /* '*' => '[' */ { 0x2d, 0x5c }, /* '-' => '\\' */ { 0x30, 0x7d }, /* '0' => '}' */ { 0x32, 0x40 }, /* '2' => '@' */ @@ -44,7 +48,9 @@ static struct keymap_key se_altgr[] = { { 0x37, 0x7b }, /* '7' => '{' */ { 0x38, 0x5b }, /* '8' => '[' */ { 0x39, 0x5d }, /* '9' => ']' */ + { 0x51, 0x40 }, /* 'Q' => '@' */ { 0x5d, 0x7e }, /* ']' => '~' */ + { 0x5f, 0x5c }, /* '_' => '\\' */ { 0x71, 0x40 }, /* 'q' => '@' */ { 0xdc, 0x7c }, /* Pseudo-'\\' => '|' */ { 0, 0 } diff --git a/src/hci/keymap/keymap_sg.c b/src/hci/keymap/keymap_sg.c index 2afe59c61..c4200099a 100644 --- a/src/hci/keymap/keymap_sg.c +++ b/src/hci/keymap/keymap_sg.c @@ -45,12 +45,19 @@ static struct keymap_key sg_basic[] = { /** "sg" AltGr remapping */ static struct keymap_key sg_altgr[] = { + { 0x22, 0x7b }, /* '"' => '{' */ + { 0x26, 0x7c }, /* '&' => '|' */ { 0x27, 0x7b }, /* '\'' => '{' */ + { 0x2b, 0x7e }, /* '+' => '~' */ { 0x32, 0x40 }, /* '2' => '@' */ { 0x33, 0x23 }, /* '3' => '#' */ { 0x37, 0x7c }, /* '7' => '|' */ { 0x3d, 0x7e }, /* '=' => '~' */ { 0x5c, 0x7d }, /* '\\' => '}' */ + { 0x7b, 0x5b }, /* '{' => '[' */ + { 0x7c, 0x7d }, /* '|' => '}' */ + { 0x7d, 0x5d }, /* '}' => ']' */ + { 0xfc, 0x5c }, /* Pseudo-'|' => '\\' */ { 0, 0 } }; diff --git a/src/util/genkeymap.py b/src/util/genkeymap.py index 9fd987477..b70ce5f72 100755 --- a/src/util/genkeymap.py +++ b/src/util/genkeymap.py @@ -365,9 +365,11 @@ class Keymap: """AltGr remapping table""" # Construct raw mapping from source ASCII to target ASCII raw = { - source: - self.target.get((key.modifiers | KeyModifiers.ALTGR), - self.target[key.modifiers])[key.keycode].ascii + source: next((self.target[x][key.keycode].ascii + for x in (key.modifiers | KeyModifiers.ALTGR, + KeyModifiers.ALTGR, key.modifiers) + if x in self.target + and self.target[x][key.keycode].ascii), None) for source, key in self.source.inverse.items() } # Identify printable keys that are unreachable via the basic map -- cgit v1.2.3-55-g7522 From 2acdc92994e7aca397b0d24b112e4973e82e0f91 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 23 Feb 2022 22:52:08 +0000 Subject: [dns] Always start DNS queries using the first configured DNS server We currently define the active DNS server as a global variable. All queries will start by attempting to contact the active DNS server, and the active DNS server will be changed only if we fail to get a response. This effectively treats the DNS server list as expressing a weak preference ordering: we will try servers in order, but once we have found a working server we will stick with that server for as long as it continues to respond to queries. Some sites are misconfigured to hand out DNS servers that do not have a consistent worldview. For example: the site may hand out two DNS server addresses, the first being an internal DNS server (which is able to resolve names in private DNS domains) and the second being a public DNS server such as 8.8.8.8 (which will correctly return NXDOMAIN for any private DNS domains). This type of configuration is fundamentally broken and should never be used, since any DNS resolver performing a query for a name within a private DNS domain may obtain a spurious NXDOMAIN response for a valid private DNS name. Work around these broken configurations by treating the DNS server list as expressing a strong preference ordering, and always starting DNS queries from the first server in the list (rather than maintaining a global concept of the active server). This will have the debatable benefit of converting permanent spurious NXDOMAIN errors into transient spurious NXDOMAIN errors, which can at least be worked around at a higher level (e.g. by retrying a download in a loop within an iPXE script). The cost of always starting DNS queries from the first server in the list is a slight delay introduced when the first server is genuinely unavailable. This should be negligible in practice since DNS queries are relatively infrequent and the failover expiry time is short. Treating the DNS server list as a preference ordering is permitted by the language of RFC 2132, which defines DHCP option 6 as a list in which "[DNS] servers SHOULD be listed in order of preference". No specification defines a precise algorithm for how this preference order should be applied in practice: this new approach seems as good as any. Requested-by: Andreas Hammarskjöld Signed-off-by: Michael Brown --- src/net/udp/dns.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/net/udp/dns.c b/src/net/udp/dns.c index e5579174e..f46eeb5c8 100644 --- a/src/net/udp/dns.c +++ b/src/net/udp/dns.c @@ -87,9 +87,6 @@ static struct dns_server dns6; /** Total number of DNS servers */ static unsigned int dns_count; -/** Current DNS server index */ -static unsigned int dns_index; - /** The DNS search list */ static struct dns_name dns_search; @@ -489,6 +486,8 @@ struct dns_request { size_t offset; /** Search list */ struct dns_name search; + /** Server index */ + unsigned int index; /** Recursion counter */ unsigned int recursion; }; @@ -606,7 +605,7 @@ static int dns_send_packet ( struct dns_request *dns ) { DBGC ( dns, "DNS %p lost DNS servers mid query\n", dns ); return -EINVAL; } - index = ( dns_index % dns_count ); + index = ( dns->index % dns_count ); if ( index < dns6.count ) { nameserver.sin6.sin6_family = AF_INET6; memcpy ( &nameserver.sin6.sin6_addr, &dns6.in6[index], @@ -651,7 +650,7 @@ static void dns_timer_expired ( struct retry_timer *timer, int fail ) { /* Move to next DNS server if this is a retransmission */ if ( dns->buf.query.id ) - dns_index++; + dns->index++; /* Send DNS query */ dns_send_packet ( dns ); -- cgit v1.2.3-55-g7522 From 3cd3a7326178bd10fb38e09eb702b27bc463d3c6 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 28 Feb 2022 13:37:40 +0000 Subject: [utf8] Add ability to accumulate Unicode characters from UTF-8 bytes Signed-off-by: Michael Brown --- src/core/utf8.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++ src/include/ipxe/utf8.h | 69 ++++++++++++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 src/core/utf8.c create mode 100644 src/include/ipxe/utf8.h diff --git a/src/core/utf8.c b/src/core/utf8.c new file mode 100644 index 000000000..4ee01baf9 --- /dev/null +++ b/src/core/utf8.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2022 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** @file + * + * UTF-8 Unicode encoding + * + */ + +/** + * Accumulate Unicode character from UTF-8 byte sequence + * + * @v utf8 UTF-8 accumulator + * @v byte UTF-8 byte + * @ret character Unicode character, or 0 if incomplete + */ +unsigned int utf8_accumulate ( struct utf8_accumulator *utf8, uint8_t byte ) { + static unsigned int min[] = { + UTF8_MIN_TWO, + UTF8_MIN_THREE, + UTF8_MIN_FOUR, + }; + unsigned int shift; + unsigned int len; + uint8_t tmp; + + /* Handle continuation bytes */ + if ( UTF8_IS_CONTINUATION ( byte ) ) { + + /* Fail if this is an unexpected continuation byte */ + if ( utf8->remaining == 0 ) { + DBGC ( utf8, "UTF8 %p unexpected %02x\n", utf8, byte ); + return UTF8_INVALID; + } + + /* Apply continuation byte */ + utf8->character <<= UTF8_CONTINUATION_BITS; + utf8->character |= ( byte & UTF8_CONTINUATION_MASK ); + + /* Return 0 if more continuation bytes are expected */ + if ( --utf8->remaining != 0 ) + return 0; + + /* Fail if sequence is illegal */ + if ( utf8->character < utf8->min ) { + DBGC ( utf8, "UTF8 %p illegal %02x\n", utf8, + utf8->character ); + return UTF8_INVALID; + } + + /* Sanity check */ + assert ( utf8->character != 0 ); + + /* Return completed character */ + DBGC2 ( utf8, "UTF8 %p accumulated %02x\n", + utf8, utf8->character ); + return utf8->character; + } + + /* Reset state and report failure if this is an unexpected + * non-continuation byte. Do not return UTF8_INVALID since + * doing so could cause us to drop a valid ASCII character. + */ + if ( utf8->remaining != 0 ) { + shift = ( utf8->remaining * UTF8_CONTINUATION_BITS ); + DBGC ( utf8, "UTF8 %p unexpected %02x (partial %02x/%02x)\n", + utf8, byte, ( utf8->character << shift ), + ( ( 1 << shift ) - 1 ) ); + utf8->remaining = 0; + } + + /* Handle initial bytes */ + if ( ! UTF8_IS_ASCII ( byte ) ) { + + /* Sanity check */ + assert ( utf8->remaining == 0 ); + + /* Count total number of bytes in sequence */ + tmp = byte; + len = 0; + while ( tmp & UTF8_HIGH_BIT ) { + tmp <<= 1; + len++; + } + + /* Check for illegal length */ + if ( len > UTF8_MAX_LEN ) { + DBGC ( utf8, "UTF8 %p illegal %02x length %d\n", + utf8, byte, len ); + return UTF8_INVALID; + } + + /* Store initial bits of character */ + utf8->character = ( tmp >> len ); + + /* Store number of bytes remaining */ + len--; + utf8->remaining = len; + assert ( utf8->remaining > 0 ); + + /* Store minimum legal value */ + utf8->min = min[ len - 1 ]; + assert ( utf8->min > 0 ); + + /* Await continuation bytes */ + return 0; + } + + /* Handle ASCII bytes */ + return byte; +} diff --git a/src/include/ipxe/utf8.h b/src/include/ipxe/utf8.h new file mode 100644 index 000000000..299c25511 --- /dev/null +++ b/src/include/ipxe/utf8.h @@ -0,0 +1,69 @@ +#ifndef _IPXE_UTF8_H +#define _IPXE_UTF8_H + +/** @file + * + * UTF-8 Unicode encoding + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include + +/** Maximum length of UTF-8 sequence */ +#define UTF8_MAX_LEN 4 + +/** Minimum legal value for two-byte UTF-8 sequence */ +#define UTF8_MIN_TWO 0x80 + +/** Minimum legal value for three-byte UTF-8 sequence */ +#define UTF8_MIN_THREE 0x800 + +/** Minimum legal value for four-byte UTF-8 sequence */ +#define UTF8_MIN_FOUR 0x10000 + +/** High bit of UTF-8 bytes */ +#define UTF8_HIGH_BIT 0x80 + +/** Number of data bits in each continuation byte */ +#define UTF8_CONTINUATION_BITS 6 + +/** Bit mask for data bits in a continuation byte */ +#define UTF8_CONTINUATION_MASK ( ( 1 << UTF8_CONTINUATION_BITS ) - 1 ) + +/** Non-data bits in a continuation byte */ +#define UTF8_CONTINUATION 0x80 + +/** Check for a continuation byte + * + * @v byte UTF-8 byte + * @ret is_continuation Byte is a continuation byte + */ +#define UTF8_IS_CONTINUATION( byte ) \ + ( ( (byte) & ~UTF8_CONTINUATION_MASK ) == UTF8_CONTINUATION ) + +/** Check for an ASCII byte + * + * @v byte UTF-8 byte + * @ret is_ascii Byte is an ASCII byte + */ +#define UTF8_IS_ASCII( byte ) ( ! ( (byte) & UTF8_HIGH_BIT ) ) + +/** Invalid character returned when decoding fails */ +#define UTF8_INVALID 0xfffd + +/** A UTF-8 character accumulator */ +struct utf8_accumulator { + /** Character in progress */ + unsigned int character; + /** Number of remaining continuation bytes */ + unsigned int remaining; + /** Minimum legal character */ + unsigned int min; +}; + +extern unsigned int utf8_accumulate ( struct utf8_accumulator *utf8, + uint8_t byte ); + +#endif /* _IPXE_UTF8_H */ -- cgit v1.2.3-55-g7522 From 7e9631b60fdcb02f05a80983ca68c10f26e4ab33 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 28 Feb 2022 14:41:45 +0000 Subject: [utf8] Add UTF-8 accumulation self-tests Signed-off-by: Michael Brown --- src/tests/tests.c | 1 + src/tests/utf8_test.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 src/tests/utf8_test.c diff --git a/src/tests/tests.c b/src/tests/tests.c index 1cc4c81e8..4dd4adf84 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -75,3 +75,4 @@ REQUIRE_OBJECT ( pem_test ); REQUIRE_OBJECT ( ntlm_test ); REQUIRE_OBJECT ( zlib_test ); REQUIRE_OBJECT ( gzip_test ); +REQUIRE_OBJECT ( utf8_test ); diff --git a/src/tests/utf8_test.c b/src/tests/utf8_test.c new file mode 100644 index 000000000..1996915cb --- /dev/null +++ b/src/tests/utf8_test.c @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2022 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * UTF-8 Unicode encoding tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include + +/** A UTF-8 accumulation test */ +struct utf8_accumulate_test { + /** UTF-8 byte string */ + const char *bytes; + /** Expected character sequence */ + const unsigned int *expected; + /** Length */ + size_t len; +}; + +/** Define inline data */ +#define DATA(...) { __VA_ARGS__ } + +/** Define a UTF-8 accumulation test */ +#define UTF8_ACCUMULATE( name, BYTES, EXPECTED ) \ + static const char name ## _bytes[] = BYTES; \ + static const unsigned int name ## _expected[] = EXPECTED; \ + static struct utf8_accumulate_test name = { \ + .bytes = name ## _bytes, \ + .expected = name ## _expected, \ + .len = ( sizeof ( name ## _expected ) / \ + sizeof ( name ## _expected[0] ) ), \ + }; + +/** Basic ASCII test */ +UTF8_ACCUMULATE ( ascii, "Hello world!", + DATA ( 'H', 'e', 'l', 'l', 'o', ' ', + 'w', 'o', 'r', 'l', 'd', '!' ) ); + +/** Multi-byte character test */ +UTF8_ACCUMULATE ( multibyte, "Héllô wörld 🥳", + DATA ( 'H', 0, L'é', 'l', 'l', 0, L'ô', ' ', + 'w', 0, L'ö', 'r', 'l', 'd', ' ', + 0, 0, 0, 0x1f973 ) ); + +/** Stray continuation byte test */ +UTF8_ACCUMULATE ( stray_continuation, + DATA ( 'a', 0x81, 'b', 0xc3, 0x82, 0x83, 'c' ), + DATA ( 'a', 0xfffd, 'b', 0, 0xc2, 0xfffd, 'c' ) ); + +/** Missing continuation byte test */ +UTF8_ACCUMULATE ( missing_continuation, + DATA ( 'a', 0xc3, 'b', 0xe1, 0x86, 0xc3, 0x89, 'c' ), + DATA ( 'a', 0, 'b', 0, 0, 0, 0xc9, 'c' ) ); + +/** Illegal two-byte sequence test */ +UTF8_ACCUMULATE ( illegal_two, + DATA ( 'a', 0xc2, 0x80, 'b', 0xc1, 0xbf, 'c', 0xc0, 0x80, + 'd' ), + DATA ( 'a', 0, 0x80, 'b', 0, 0xfffd, 'c', 0, 0xfffd, 'd' ) ); + +/** Illegal three-byte sequence test */ +UTF8_ACCUMULATE ( illegal_three, + DATA ( 'a', 0xe0, 0xa0, 0x80, 'b', 0xe0, 0x9f, 0xbf, 'c', + 0xe0, 0x80, 0x80, 'd' ), + DATA ( 'a', 0, 0, 0x800, 'b', 0, 0, 0xfffd, 'c', + 0, 0, 0xfffd, 'd' ) ); + +/** Illegal four-byte sequence test */ +UTF8_ACCUMULATE ( illegal_four, + DATA ( 'a', 0xf0, 0x90, 0x80, 0x80, 'b', 0xf0, 0x8f, 0xbf, + 0xbf, 'c', 0xf0, 0x80, 0x80, 0x80, 'd' ), + DATA ( 'a', 0, 0, 0, 0x10000, 'b', 0, 0, 0, 0xfffd, 'c', + 0, 0, 0, 0xfffd, 'd' ) ); + +/** Illegal overlength sequence test */ +UTF8_ACCUMULATE ( illegal_length, + DATA ( 'a', 0xf8, 0xbf, 0xbf, 0xbf, 0xbf, 'b', 0xfc, 0xbf, + 0xbf, 0xbf, 0xbf, 0xbf, 'c', 0xfe, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 'd', 0xff, 0xbf, 0xbf, 0xbf, 0xbf, + 0xbf, 0xbf, 0xbf, 'e' ), + DATA ( 'a', 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 'b', + 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 'c', + 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, + 0xfffd, 'd', 0xfffd, 0xfffd, 0xfffd, 0xfffd, 0xfffd, + 0xfffd, 0xfffd, 0xfffd, 'e' ) ); + +/** + * Report UTF-8 accumulation test result + * + * @v test UTF-8 accumulation test + * @v file Test code file + * @v line Test code line + */ +static void utf8_accumulate_okx ( struct utf8_accumulate_test *test, + const char *file, unsigned int line ) { + struct utf8_accumulator utf8; + unsigned int character; + unsigned int i; + + /* Initialise accumulator */ + memset ( &utf8, 0, sizeof ( utf8 ) ); + + /* Test each byte in turn */ + for ( i = 0 ; i < test->len ; i++ ) { + character = utf8_accumulate ( &utf8, test->bytes[i] ); + DBGC ( test, "UTF8 byte %02x character %02x\n", + test->bytes[i], character ); + okx ( character == test->expected[i], file, line ); + } +} +#define utf8_accumulate_ok( test ) \ + utf8_accumulate_okx ( test, __FILE__, __LINE__ ) + +/** + * Perform UTF-8 self-test + * + */ +static void utf8_test_exec ( void ) { + + /* Accumulation tests */ + utf8_accumulate_ok ( &ascii ); + utf8_accumulate_ok ( &multibyte ); + utf8_accumulate_ok ( &stray_continuation ); + utf8_accumulate_ok ( &missing_continuation ); + utf8_accumulate_ok ( &illegal_two ); + utf8_accumulate_ok ( &illegal_three ); + utf8_accumulate_ok ( &illegal_four ); + utf8_accumulate_ok ( &illegal_length ); +} + +/** UTF-8 self-test */ +struct self_test utf8_test __self_test = { + .name = "utf8", + .exec = utf8_test_exec, +}; -- cgit v1.2.3-55-g7522 From 2ff3385e0078edda43e13ebfc9978fbcc5db311a Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 28 Feb 2022 13:39:10 +0000 Subject: [efi] Support Unicode character output via text console Signed-off-by: Michael Brown --- src/interface/efi/efi_console.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/interface/efi/efi_console.c b/src/interface/efi/efi_console.c index fc1500afb..04bbd9e0c 100644 --- a/src/interface/efi/efi_console.c +++ b/src/interface/efi/efi_console.c @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include #include #include +#include #include #include #include @@ -201,6 +202,9 @@ static struct ansiesc_context efi_ansiesc_ctx = { .handlers = efi_ansiesc_handlers, }; +/** EFI console UTF-8 accumulator */ +static struct utf8_accumulator efi_utf8_acc; + /** * Print a character to EFI console * @@ -208,13 +212,25 @@ static struct ansiesc_context efi_ansiesc_ctx = { */ static void efi_putchar ( int character ) { EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut; - wchar_t wstr[] = { character, 0 }; + wchar_t wstr[2]; /* Intercept ANSI escape sequences */ character = ansiesc_process ( &efi_ansiesc_ctx, character ); if ( character < 0 ) return; + /* Accumulate Unicode characters */ + character = utf8_accumulate ( &efi_utf8_acc, character ); + if ( character == 0 ) + return; + + /* Treat unrepresentable (non-UCS2) characters as invalid */ + if ( character & ~( ( wchar_t ) -1UL ) ) + character = UTF8_INVALID; + + /* Output character */ + wstr[0] = character; + wstr[1] = L'\0'; conout->OutputString ( conout, wstr ); } -- cgit v1.2.3-55-g7522 From ba93c9134ce9d9edcba117b690fbbdd35b3e066b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 14 Mar 2022 22:38:24 +0000 Subject: [fbcon] Support Unicode character output Accumulate UTF-8 characters in fbcon_putchar(), and require the frame buffer console's .glyph() method to accept Unicode character values. Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/vesafb.c | 25 ++++- src/core/fbcon.c | 5 + src/include/ipxe/fbcon.h | 7 +- src/interface/efi/efi_fbcon.c | 198 +++++++++++++++++++++------------ 4 files changed, 161 insertions(+), 74 deletions(-) diff --git a/src/arch/x86/interface/pcbios/vesafb.c b/src/arch/x86/interface/pcbios/vesafb.c index 50e485852..86edbda42 100644 --- a/src/arch/x86/interface/pcbios/vesafb.c +++ b/src/arch/x86/interface/pcbios/vesafb.c @@ -78,6 +78,15 @@ struct console_driver bios_console __attribute__ (( weak )); /** Font corresponding to selected character width and height */ #define VESAFB_FONT VBE_FONT_8x16 +/** Number of ASCII glyphs within the font */ +#define VESAFB_ASCII 128 + +/** Glyph to render for non-ASCII characters + * + * We choose to use one of the box-drawing glyphs. + */ +#define VESAFB_UNKNOWN 0xfe + /* Forward declaration */ struct console_driver vesafb_console __console_driver; @@ -130,12 +139,24 @@ static int vesafb_rc ( unsigned int status ) { /** * Get character glyph * - * @v character Character + * @v character Unicode character * @v glyph Character glyph to fill in */ static void vesafb_glyph ( unsigned int character, uint8_t *glyph ) { - size_t offset = ( character * VESAFB_CHAR_HEIGHT ); + unsigned int index; + size_t offset; + + /* Identify glyph */ + if ( character < VESAFB_ASCII ) { + /* ASCII character: use corresponding glyph */ + index = character; + } else { + /* Non-ASCII character: use "unknown" glyph */ + index = VESAFB_UNKNOWN; + } + /* Copy glyph from BIOS font table */ + offset = ( index * VESAFB_CHAR_HEIGHT ); copy_from_real ( glyph, vesafb.glyphs.segment, ( vesafb.glyphs.offset + offset ), VESAFB_CHAR_HEIGHT); } diff --git a/src/core/fbcon.c b/src/core/fbcon.c index 44a56e105..ff3132ac7 100644 --- a/src/core/fbcon.c +++ b/src/core/fbcon.c @@ -446,6 +446,11 @@ void fbcon_putchar ( struct fbcon *fbcon, int character ) { if ( character < 0 ) return; + /* Accumulate Unicode characters */ + character = utf8_accumulate ( &fbcon->utf8, character ); + if ( character == 0 ) + return; + /* Handle control characters */ switch ( character ) { case '\r': diff --git a/src/include/ipxe/fbcon.h b/src/include/ipxe/fbcon.h index 42ffca3d7..a4c7a9ab3 100644 --- a/src/include/ipxe/fbcon.h +++ b/src/include/ipxe/fbcon.h @@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +#include #include #include @@ -36,7 +37,7 @@ struct fbcon_font { /** * Get character glyph * - * @v character Character + * @v character Unicode character * @v glyph Character glyph to fill in */ void ( * glyph ) ( unsigned int character, uint8_t *glyph ); @@ -92,7 +93,7 @@ struct fbcon_text_cell { uint32_t foreground; /** Background colour */ uint32_t background; - /** Character */ + /** Unicode character */ unsigned int character; }; @@ -138,6 +139,8 @@ struct fbcon { unsigned int ypos; /** ANSI escape sequence context */ struct ansiesc_context ctx; + /** UTF-8 accumulator */ + struct utf8_accumulator utf8; /** Text array */ struct fbcon_text text; /** Background picture */ diff --git a/src/interface/efi/efi_fbcon.c b/src/interface/efi/efi_fbcon.c index abc5a9390..d9e3e69e6 100644 --- a/src/interface/efi/efi_fbcon.c +++ b/src/interface/efi/efi_fbcon.c @@ -62,6 +62,9 @@ struct console_driver efi_console __attribute__ (( weak )); #define CONSOLE_EFIFB ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG ) #endif +/** Number of ASCII glyphs in cache */ +#define EFIFB_ASCII 128 + /* Forward declaration */ struct console_driver efifb_console __console_driver; @@ -84,7 +87,7 @@ struct efifb { struct fbcon_colour_map map; /** Font definition */ struct fbcon_font font; - /** Character glyphs */ + /** Character glyph cache */ userptr_t glyphs; }; @@ -92,14 +95,112 @@ struct efifb { static struct efifb efifb; /** - * Get character glyph + * Draw character glyph * * @v character Character + * @v index Index within glyph cache + * @v toggle Bits to toggle in each bitmask + * @ret height Character height, or negative error + */ +static int efifb_draw ( unsigned int character, unsigned int index, + unsigned int toggle ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_IMAGE_OUTPUT *blt; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *pixel; + unsigned int height; + unsigned int x; + unsigned int y; + uint8_t bitmask; + size_t offset; + EFI_STATUS efirc; + int rc; + + /* Clear existing glyph */ + offset = ( index * efifb.font.height ); + memset_user ( efifb.glyphs, offset, 0, efifb.font.height ); + + /* Get glyph */ + blt = NULL; + if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont, character, + NULL, &blt, NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efifb, "EFIFB could not get glyph %#02x: %s\n", + character, strerror ( rc ) ); + goto err_get; + } + assert ( blt != NULL ); + + /* Sanity check */ + if ( blt->Width > 8 ) { + DBGC ( &efifb, "EFIFB glyph %#02x invalid width %d\n", + character, blt->Width ); + rc = -EINVAL; + goto err_width; + } + + /* Convert glyph to bitmap */ + pixel = blt->Image.Bitmap; + height = blt->Height; + for ( y = 0 ; ( ( y < height ) && ( y < efifb.font.height ) ) ; y++ ) { + bitmask = 0; + for ( x = 0 ; x < blt->Width ; x++ ) { + bitmask = rol8 ( bitmask, 1 ); + if ( pixel->Blue || pixel->Green || pixel->Red ) + bitmask |= 0x01; + pixel++; + } + bitmask ^= toggle; + copy_to_user ( efifb.glyphs, offset++, &bitmask, + sizeof ( bitmask ) ); + } + + /* Free glyph */ + bs->FreePool ( blt ); + + return height; + + err_width: + bs->FreePool ( blt ); + err_get: + return rc; +} + +/** + * Draw "unknown character" glyph + * + * @v index Index within glyph cache + * @ret rc Return status code + */ +static int efifb_draw_unknown ( unsigned int index ) { + + /* Draw an inverted '?' glyph */ + return efifb_draw ( '?', index, -1U ); +} + +/** + * Get character glyph + * + * @v character Unicode character * @v glyph Character glyph to fill in */ static void efifb_glyph ( unsigned int character, uint8_t *glyph ) { - size_t offset = ( character * efifb.font.height ); + unsigned int index; + size_t offset; + + /* Identify glyph */ + if ( character < EFIFB_ASCII ) { + + /* ASCII character: use fixed cache entry */ + index = character; + } else { + + /* Non-ASCII character: use an "unknown" glyph */ + index = 0; + } + + /* Copy cached glyph */ + offset = ( index * efifb.font.height ); copy_from_user ( glyph, efifb.glyphs, offset, efifb.font.height ); } @@ -109,16 +210,10 @@ static void efifb_glyph ( unsigned int character, uint8_t *glyph ) { * @ret rc Return status code */ static int efifb_glyphs ( void ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_IMAGE_OUTPUT *blt; - EFI_GRAPHICS_OUTPUT_BLT_PIXEL *pixel; - size_t offset; - size_t len; - uint8_t bitmask; unsigned int character; - unsigned int x; - unsigned int y; - EFI_STATUS efirc; + int height; + int max; + size_t len; int rc; /* Get font height. The GetFontInfo() call nominally returns @@ -128,38 +223,32 @@ static int efifb_glyphs ( void ) { * height. */ efifb.font.height = 0; - for ( character = 0 ; character < 256 ; character++ ) { + max = 0; + for ( character = 0 ; character < EFIFB_ASCII ; character++ ) { /* Skip non-printable characters */ if ( ! isprint ( character ) ) continue; /* Get glyph */ - blt = NULL; - if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont, - character, NULL, &blt, - NULL ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( &efifb, "EFIFB could not get glyph %d: %s\n", - character, strerror ( rc ) ); - continue; + height = efifb_draw ( character, 0, 0 ); + if ( height < 0 ) { + rc = height; + goto err_height; } - assert ( blt != NULL ); /* Calculate maximum height */ - if ( efifb.font.height < blt->Height ) - efifb.font.height = blt->Height; - - /* Free glyph */ - bs->FreePool ( blt ); + if ( max < height ) + max = height; } - if ( ! efifb.font.height ) { + if ( ! max ) { DBGC ( &efifb, "EFIFB could not get font height\n" ); return -ENOENT; } + efifb.font.height = max; /* Allocate glyph data */ - len = ( 256 * efifb.font.height * sizeof ( bitmask ) ); + len = ( EFIFB_ASCII * efifb.font.height ); efifb.glyphs = umalloc ( len ); if ( ! efifb.glyphs ) { rc = -ENOMEM; @@ -168,60 +257,29 @@ static int efifb_glyphs ( void ) { memset_user ( efifb.glyphs, 0, 0, len ); /* Get font data */ - for ( character = 0 ; character < 256 ; character++ ) { + for ( character = 0 ; character < EFIFB_ASCII ; character++ ) { /* Skip non-printable characters */ - if ( ! isprint ( character ) ) - continue; - - /* Get glyph */ - blt = NULL; - if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont, - character, NULL, &blt, - NULL ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( &efifb, "EFIFB could not get glyph %d: %s\n", - character, strerror ( rc ) ); - continue; - } - assert ( blt != NULL ); - - /* Sanity check */ - if ( blt->Width > 8 ) { - DBGC ( &efifb, "EFIFB glyph %d invalid width %d\n", - character, blt->Width ); - continue; - } - if ( blt->Height > efifb.font.height ) { - DBGC ( &efifb, "EFIFB glyph %d invalid height %d\n", - character, blt->Height ); + if ( ! isprint ( character ) ) { + efifb_draw_unknown ( character ); continue; } - /* Convert glyph to bitmap */ - pixel = blt->Image.Bitmap; - offset = ( character * efifb.font.height ); - for ( y = 0 ; y < blt->Height ; y++ ) { - bitmask = 0; - for ( x = 0 ; x < blt->Width ; x++ ) { - bitmask = rol8 ( bitmask, 1 ); - if ( pixel->Blue || pixel->Green || pixel->Red ) - bitmask |= 0x01; - pixel++; - } - copy_to_user ( efifb.glyphs, offset++, &bitmask, - sizeof ( bitmask ) ); + /* Get glyph */ + height = efifb_draw ( character, character, 0 ); + if ( height < 0 ) { + rc = height; + goto err_draw; } - - /* Free glyph */ - bs->FreePool ( blt ); } efifb.font.glyph = efifb_glyph; return 0; + err_draw: ufree ( efifb.glyphs ); err_alloc: + err_height: return rc; } -- cgit v1.2.3-55-g7522 From dd3547543811dd2e996b910bdadb609414904352 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Tue, 15 Mar 2022 15:04:22 +0000 Subject: [efi] Support Unicode character output via framebuffer console Extend the glyph cache to include a number of dynamic entries that are populated on demand whenever a non-ASCII character needs to be drawn. Signed-off-by: Michael Brown --- src/interface/efi/efi_fbcon.c | 51 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/src/interface/efi/efi_fbcon.c b/src/interface/efi/efi_fbcon.c index d9e3e69e6..d388e0317 100644 --- a/src/interface/efi/efi_fbcon.c +++ b/src/interface/efi/efi_fbcon.c @@ -65,6 +65,9 @@ struct console_driver efi_console __attribute__ (( weak )); /** Number of ASCII glyphs in cache */ #define EFIFB_ASCII 128 +/** Number of dynamic non-ASCII glyphs in cache */ +#define EFIFB_DYNAMIC 32 + /* Forward declaration */ struct console_driver efifb_console __console_driver; @@ -89,6 +92,10 @@ struct efifb { struct fbcon_font font; /** Character glyph cache */ userptr_t glyphs; + /** Dynamic characters in cache */ + unsigned int dynamic[EFIFB_DYNAMIC]; + /** Next dynamic character cache entry to evict */ + unsigned int next; }; /** The EFI frame buffer */ @@ -177,6 +184,41 @@ static int efifb_draw_unknown ( unsigned int index ) { return efifb_draw ( '?', index, -1U ); } +/** + * Get dynamic glyph index + * + * @v character Unicode character + * @ret index Glyph cache index + */ +static unsigned int efifb_dynamic ( unsigned int character ) { + unsigned int dynamic; + unsigned int index; + unsigned int i; + int height; + + /* Search existing cached entries */ + for ( i = 0 ; i < EFIFB_DYNAMIC ; i++ ) { + if ( character == efifb.dynamic[i] ) + return ( EFIFB_ASCII + i ); + } + + /* Overwrite the oldest cache entry */ + dynamic = ( efifb.next++ % EFIFB_DYNAMIC ); + index = ( EFIFB_ASCII + dynamic ); + DBGC2 ( &efifb, "EFIFB dynamic %#02x is glyph %#02x\n", + dynamic, character ); + + /* Draw glyph */ + height = efifb_draw ( character, index, 0 ); + if ( height < 0 ) + efifb_draw_unknown ( index ); + + /* Record cached character */ + efifb.dynamic[dynamic] = character; + + return index; +} + /** * Get character glyph * @@ -195,8 +237,8 @@ static void efifb_glyph ( unsigned int character, uint8_t *glyph ) { } else { - /* Non-ASCII character: use an "unknown" glyph */ - index = 0; + /* Non-ASCII character: use dynamic glyph cache */ + index = efifb_dynamic ( character ); } /* Copy cached glyph */ @@ -248,7 +290,7 @@ static int efifb_glyphs ( void ) { efifb.font.height = max; /* Allocate glyph data */ - len = ( EFIFB_ASCII * efifb.font.height ); + len = ( ( EFIFB_ASCII + EFIFB_DYNAMIC ) * efifb.font.height ); efifb.glyphs = umalloc ( len ); if ( ! efifb.glyphs ) { rc = -ENOMEM; @@ -273,6 +315,9 @@ static int efifb_glyphs ( void ) { } } + /* Clear dynamic glyph character cache */ + memset ( efifb.dynamic, 0, sizeof ( efifb.dynamic ) ); + efifb.font.glyph = efifb_glyph; return 0; -- cgit v1.2.3-55-g7522 From 27825e555746c379ac045466f692ed77686af2b5 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 23 Mar 2022 14:39:11 +0000 Subject: [acpi] Allow for the possibility of overriding ACPI tables at link time Allow for linked-in code to override the mechanism used to locate an ACPI table, thereby opening up the possibility of ACPI self-tests. Signed-off-by: Michael Brown --- src/arch/x86/interface/pcbios/acpi_timer.c | 2 +- src/arch/x86/interface/pcbios/acpipwr.c | 2 +- src/core/acpi.c | 22 ++++++++++++++++++++-- src/core/acpi_settings.c | 2 +- src/include/ipxe/acpi.h | 3 +++ 5 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/arch/x86/interface/pcbios/acpi_timer.c b/src/arch/x86/interface/pcbios/acpi_timer.c index 82e85a034..2e4047e38 100644 --- a/src/arch/x86/interface/pcbios/acpi_timer.c +++ b/src/arch/x86/interface/pcbios/acpi_timer.c @@ -107,7 +107,7 @@ static int acpi_timer_probe ( void ) { unsigned int pm_tmr_blk; /* Locate FADT */ - fadt = acpi_find ( FADT_SIGNATURE, 0 ); + fadt = acpi_table ( FADT_SIGNATURE, 0 ); if ( ! fadt ) { DBGC ( &acpi_timer, "ACPI could not find FADT\n" ); return -ENOENT; diff --git a/src/arch/x86/interface/pcbios/acpipwr.c b/src/arch/x86/interface/pcbios/acpipwr.c index 3dac6b605..f08b4af25 100644 --- a/src/arch/x86/interface/pcbios/acpipwr.c +++ b/src/arch/x86/interface/pcbios/acpipwr.c @@ -123,7 +123,7 @@ int acpi_poweroff ( void ) { int rc; /* Locate FADT */ - fadt = acpi_find ( FADT_SIGNATURE, 0 ); + fadt = acpi_table ( FADT_SIGNATURE, 0 ); if ( ! fadt ) { DBGC ( colour, "ACPI could not find FADT\n" ); return -ENOENT; diff --git a/src/core/acpi.c b/src/core/acpi.c index aa486da93..526bf8555 100644 --- a/src/core/acpi.c +++ b/src/core/acpi.c @@ -38,6 +38,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Colour for debug messages */ #define colour FADT_SIGNATURE +/** ACPI table finder + * + * May be overridden at link time to inject tables for testing. + */ +typeof ( acpi_find ) *acpi_finder __attribute__ (( weak )) = acpi_find; + /****************************************************************************** * * Utility functions @@ -82,6 +88,18 @@ void acpi_fix_checksum ( struct acpi_header *acpi ) { acpi->checksum -= acpi_checksum ( virt_to_user ( acpi ) ); } +/** + * Locate ACPI table + * + * @v signature Requested table signature + * @v index Requested index of table with this signature + * @ret table Table, or UNULL if not found + */ +userptr_t acpi_table ( uint32_t signature, unsigned int index ) { + + return ( *acpi_finder ) ( signature, index ); +} + /** * Locate ACPI table via RSDT * @@ -230,7 +248,7 @@ int acpi_extract ( uint32_t signature, void *data, int rc; /* Try DSDT first */ - fadt = acpi_find ( FADT_SIGNATURE, 0 ); + fadt = acpi_table ( FADT_SIGNATURE, 0 ); if ( fadt ) { copy_from_user ( &fadtab, fadt, 0, sizeof ( fadtab ) ); dsdt = phys_to_user ( fadtab.dsdt ); @@ -241,7 +259,7 @@ int acpi_extract ( uint32_t signature, void *data, /* Try all SSDTs */ for ( i = 0 ; ; i++ ) { - ssdt = acpi_find ( SSDT_SIGNATURE, i ); + ssdt = acpi_table ( SSDT_SIGNATURE, i ); if ( ! ssdt ) break; if ( ( rc = acpi_zsdt ( ssdt, signature, data, diff --git a/src/core/acpi_settings.c b/src/core/acpi_settings.c index 7ba2e979f..b9e2b7f61 100644 --- a/src/core/acpi_settings.c +++ b/src/core/acpi_settings.c @@ -88,7 +88,7 @@ static int acpi_settings_fetch ( struct settings *settings, acpi_name ( tag_signature ), tag_index, tag_offset, tag_len ); /* Locate ACPI table */ - table = acpi_find ( tag_signature, tag_index ); + table = acpi_table ( tag_signature, tag_index ); if ( ! table ) return -ENOENT; diff --git a/src/include/ipxe/acpi.h b/src/include/ipxe/acpi.h index 7df3ec21c..c34681238 100644 --- a/src/include/ipxe/acpi.h +++ b/src/include/ipxe/acpi.h @@ -386,7 +386,10 @@ acpi_describe ( struct interface *interface ); #define acpi_describe_TYPE( object_type ) \ typeof ( struct acpi_descriptor * ( object_type ) ) +extern userptr_t ( * acpi_finder ) ( uint32_t signature, unsigned int index ); + extern void acpi_fix_checksum ( struct acpi_header *acpi ); +extern userptr_t acpi_table ( uint32_t signature, unsigned int index ); extern int acpi_extract ( uint32_t signature, void *data, int ( * extract ) ( userptr_t zsdt, size_t len, size_t offset, void *data ) ); -- cgit v1.2.3-55-g7522 From 1e1b9593e6f7d6026f2d6a2109e06a9629ef0ce0 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 23 Mar 2022 14:41:36 +0000 Subject: [linux] Add stub phys_to_user() implementation For symmetry with the stub user_to_phys() implementation, provide phys_to_user() with the same underlying assumption that virtual addresses are physical (since there is no way to know the real physical address when running as a Linux userspace executable). Signed-off-by: Michael Brown --- src/include/ipxe/linux/linux_uaccess.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/include/ipxe/linux/linux_uaccess.h b/src/include/ipxe/linux/linux_uaccess.h index acd919a85..a642b6163 100644 --- a/src/include/ipxe/linux/linux_uaccess.h +++ b/src/include/ipxe/linux/linux_uaccess.h @@ -25,7 +25,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #endif /** - * Convert user buffer to physical address + * Convert user pointer to physical address * * @v userptr User pointer * @v offset Offset from user pointer @@ -46,6 +46,19 @@ UACCESS_INLINE ( linux, user_to_phys ) ( userptr_t userptr, off_t offset ) { return ( userptr + offset ); } +/** + * Convert physical address to user pointer + * + * @v phys_addr Physical address + * @ret userptr User pointer + */ +static inline __always_inline userptr_t +UACCESS_INLINE ( linux, phys_to_user ) ( physaddr_t phys_addr ) { + + /* For symmetry with the stub user_to_phys() */ + return phys_addr; +} + static inline __always_inline userptr_t UACCESS_INLINE ( linux, virt_to_user ) ( volatile const void *addr ) { return trivial_virt_to_user ( addr ); -- cgit v1.2.3-55-g7522 From 614c3f43a12cd6be6d79d7a8a63cfa603ff9d49f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 23 Mar 2022 14:57:53 +0000 Subject: [acpi] Add MAC address extraction self-tests Signed-off-by: Michael Brown --- src/tests/acpi_test.c | 238 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/tests/tests.c | 1 + 2 files changed, 239 insertions(+) create mode 100644 src/tests/acpi_test.c diff --git a/src/tests/acpi_test.c b/src/tests/acpi_test.c new file mode 100644 index 000000000..972067ee2 --- /dev/null +++ b/src/tests/acpi_test.c @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2022 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * ACPI tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include +#include +#include +#include + +/** An ACPI test table signature */ +union acpi_test_signature { + /** String */ + char str[4]; + /** Raw value */ + uint32_t raw; +}; + +/** An ACPI test table */ +struct acpi_test_table { + /** Signature */ + union acpi_test_signature signature; + /** Table content */ + const void *data; +}; + +/** An ACPI test table set */ +struct acpi_test_tables { + /** Tables */ + struct acpi_test_table **table; + /** Number of tables */ + unsigned int count; +}; + +/** An ACPI MAC extraction test */ +struct acpi_mac_test { + /** ACPI test table set */ + struct acpi_test_tables *tables; + /** Expected MAC address */ + uint8_t expected[ETH_ALEN]; +}; + +/** Define inline data */ +#define DATA(...) { __VA_ARGS__ } + +/** Define an ACPI test table */ +#define ACPI_TABLE( name, SIGNATURE, DATA ) \ + static const uint8_t name ## _data[] = DATA; \ + static struct acpi_test_table name = { \ + .signature = { \ + .str = SIGNATURE, \ + }, \ + .data = name ## _data, \ + } + +/** Define an ACPI test table set */ +#define ACPI_TABLES( name, ... ) \ + static struct acpi_test_table * name ## _table[] = \ + { __VA_ARGS__ }; \ + static struct acpi_test_tables name = { \ + .table = name ## _table, \ + .count = ( sizeof ( name ## _table ) / \ + sizeof ( name ## _table[0] ) ), \ + } + +/** Define an ACPI MAC extraction test */ +#define ACPI_MAC( name, TABLES, EXPECTED ) \ + static struct acpi_mac_test name = { \ + .tables = TABLES, \ + .expected = EXPECTED, \ + } + +/** "AMAC" SSDT + * + * DefinitionBlock ("", "SSDT", 2, "", "", 0x0) { + * Scope (\_SB) { + * Method (HW00, 0, Serialized) { Return(0) } + * Method (AMAC, 0, Serialized) { ToString("_AUXMAC_#525400aabbcc#") } + * Method (HW42, 0, Serialized) { Return(42) } + * } + * } + */ +ACPI_TABLE ( amac_ssdt, "SSDT", + DATA ( 0x53, 0x53, 0x44, 0x54, 0x5d, 0x00, 0x00, 0x00, 0x02, + 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x4e, 0x54, 0x4c, 0x04, 0x06, 0x21, 0x20, + 0x10, 0x38, 0x5c, 0x5f, 0x53, 0x42, 0x5f, 0x14, 0x08, + 0x48, 0x57, 0x30, 0x30, 0x08, 0xa4, 0x00, 0x14, 0x1e, + 0x41, 0x4d, 0x41, 0x43, 0x08, 0x0d, 0x5f, 0x41, 0x55, + 0x58, 0x4d, 0x41, 0x43, 0x5f, 0x23, 0x35, 0x32, 0x35, + 0x34, 0x30, 0x30, 0x61, 0x61, 0x62, 0x62, 0x63, 0x63, + 0x23, 0x00, 0x14, 0x09, 0x48, 0x57, 0x34, 0x32, 0x08, + 0xa4, 0x0a, 0x2a ) ); + +/** "AMAC" test tables */ +ACPI_TABLES ( amac_tables, &amac_ssdt ); + +/** "AMAC" test */ +ACPI_MAC ( amac, &amac_tables, + DATA ( 0x52, 0x54, 0x00, 0xaa, 0xbb, 0xcc ) ); + +/** "MACA" SSDT1 (does not contain AUXMAC) */ +ACPI_TABLE ( maca_ssdt1, "SSDT", + DATA ( 0x53, 0x53, 0x44, 0x54, 0x3e, 0x00, 0x00, 0x00, 0x02, + 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x4e, 0x54, 0x4c, 0x04, 0x06, 0x21, 0x20, + 0x10, 0x19, 0x5c, 0x5f, 0x53, 0x42, 0x5f, 0x14, 0x08, + 0x48, 0x57, 0x30, 0x30, 0x08, 0xa4, 0x00, 0x14, 0x09, + 0x48, 0x57, 0x34, 0x32, 0x08, 0xa4, 0x0a, 0x2a ) ); + +/** "MACA" SSDT2 (contains AUXMAC) */ +ACPI_TABLE ( maca_ssdt2, "SSDT", + DATA ( 0x53, 0x53, 0x44, 0x54, 0x54, 0x00, 0x00, 0x00, 0x02, + 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x4e, 0x54, 0x4c, 0x04, 0x06, 0x21, 0x20, + 0x10, 0x2f, 0x5c, 0x5f, 0x53, 0x42, 0x5f, 0x14, 0x1e, + 0x4d, 0x41, 0x43, 0x41, 0x08, 0x0d, 0x5f, 0x41, 0x55, + 0x58, 0x4d, 0x41, 0x43, 0x5f, 0x23, 0x35, 0x32, 0x35, + 0x34, 0x30, 0x30, 0x31, 0x31, 0x32, 0x32, 0x33, 0x33, + 0x23, 0x00, 0x14, 0x09, 0x48, 0x57, 0x39, 0x39, 0x08, + 0xa4, 0x0a, 0x63 ) ); + +/** "MACA" test tables */ +ACPI_TABLES ( maca_tables, &maca_ssdt1, &maca_ssdt2 ); + +/** "MACA" test */ +ACPI_MAC ( maca, &maca_tables, + DATA ( 0x52, 0x54, 0x00, 0x11, 0x22, 0x33 ) ); + +/** Current ACPI test table set */ +static struct acpi_test_tables *acpi_test_tables; + +/** + * Locate ACPI test table + * + * @v signature Requested table signature + * @v index Requested index of table with this signature + * @ret table Table, or UNULL if not found + */ +static userptr_t acpi_test_find ( uint32_t signature, unsigned int index ) { + struct acpi_test_table *table; + unsigned int i; + + /* Fail if no test tables are installed */ + if ( ! acpi_test_tables ) + return UNULL; + + /* Scan through test tables */ + for ( i = 0 ; i < acpi_test_tables->count ; i++ ) { + table = acpi_test_tables->table[i]; + if ( ( signature == le32_to_cpu ( table->signature.raw ) ) && + ( index-- == 0 ) ) { + return virt_to_user ( table->data ); + } + } + + return UNULL; +} + +/** Override ACPI table finder */ +typeof ( acpi_find ) *acpi_finder = acpi_test_find; + +/** + * Report ACPI MAC extraction test result + * + * @v test ACPI MAC extraction test + * @v file Test code file + * @v line Test code line + */ +static void acpi_mac_okx ( struct acpi_mac_test *test, + const char *file, unsigned int line ) { + uint8_t mac[ETH_ALEN]; + int rc; + + /* Set test table set */ + acpi_test_tables = test->tables; + + /* Extract MAC address */ + rc = acpi_mac ( mac ); + okx ( rc == 0, file, line ); + + /* Check extracted MAC address */ + okx ( memcmp ( mac, test->expected, ETH_ALEN ) == 0, file, line ); + + /* Clear test table set */ + acpi_test_tables = NULL; +} +#define acpi_mac_ok( test ) \ + acpi_mac_okx ( test, __FILE__, __LINE__ ) + +/** + * Perform ACPI self-test + * + */ +static void acpi_test_exec ( void ) { + + /* MAC extraction tests */ + acpi_mac_ok ( &amac ); + acpi_mac_ok ( &maca ); +} + +/** ACPI self-test */ +struct self_test acpi_test __self_test = { + .name = "acpi", + .exec = acpi_test_exec, +}; diff --git a/src/tests/tests.c b/src/tests/tests.c index 4dd4adf84..5dbbac1b1 100644 --- a/src/tests/tests.c +++ b/src/tests/tests.c @@ -76,3 +76,4 @@ REQUIRE_OBJECT ( ntlm_test ); REQUIRE_OBJECT ( zlib_test ); REQUIRE_OBJECT ( gzip_test ); REQUIRE_OBJECT ( utf8_test ); +REQUIRE_OBJECT ( acpi_test ); -- cgit v1.2.3-55-g7522 From f58b5109f46088bdbb5345a9d94b636c54345bdf Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 23 Mar 2022 15:02:17 +0000 Subject: [acpi] Support the "_RTXMAC_" format for ACPI-based MAC addresses Some newer HP products expose the host-based MAC (HBMAC) address using an ACPI method named "RTMA" returning a part-binary string of the form "_RTXMAC_##", where "" comprises the raw MAC address bytes. Extend the existing support to handle this format alongside the older "_AUXMAC_" format (which uses a base16-encoded MAC address). Reported-by: Andreas Hammarskjöld Tested-by: Andreas Hammarskjöld Signed-off-by: Michael Brown --- src/core/acpimac.c | 153 +++++++++++++++++++++++++++++++++++++++++--------- src/tests/acpi_test.c | 19 +++++++ 2 files changed, 144 insertions(+), 28 deletions(-) diff --git a/src/core/acpimac.c b/src/core/acpimac.c index 1cc8220b1..5920480dd 100644 --- a/src/core/acpimac.c +++ b/src/core/acpimac.c @@ -46,11 +46,79 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** MACA signature */ #define MACA_SIGNATURE ACPI_SIGNATURE ( 'M', 'A', 'C', 'A' ) -/** Maximum number of bytes to skip after AMAC/MACA signature +/** RTMA signature */ +#define RTMA_SIGNATURE ACPI_SIGNATURE ( 'R', 'T', 'M', 'A' ) + +/** Maximum number of bytes to skip after ACPI signature * * This is entirely empirical. */ -#define AUXMAC_MAX_SKIP 8 +#define ACPIMAC_MAX_SKIP 8 + +/** An ACPI MAC extraction mechanism */ +struct acpimac_extractor { + /** Prefix string */ + const char *prefix; + /** Encoded MAC length */ + size_t len; + /** Decode MAC + * + * @v mac Encoded MAC + * @v hw_addr MAC address to fill in + * @ret rc Return status code + */ + int ( * decode ) ( const char *mac, uint8_t *hw_addr ); +}; + +/** + * Decode Base16-encoded MAC address + * + * @v mac Encoded MAC + * @v hw_addr MAC address to fill in + * @ret rc Return status code + */ +static int acpimac_decode_base16 ( const char *mac, uint8_t *hw_addr ) { + int len; + int rc; + + /* Attempt to base16-decode MAC address */ + len = base16_decode ( mac, hw_addr, ETH_ALEN ); + if ( len < 0 ) { + rc = len; + DBGC ( colour, "ACPI could not decode base16 MAC \"%s\": %s\n", + mac, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Decode raw MAC address + * + * @v mac Encoded MAC + * @v hw_addr MAC address to fill in + * @ret rc Return status code + */ +static int acpimac_decode_raw ( const char *mac, uint8_t *hw_addr ) { + + memcpy ( hw_addr, mac, ETH_ALEN ); + return 0; +} + +/** "_AUXMAC_" extraction mechanism */ +static struct acpimac_extractor acpimac_auxmac = { + .prefix = "_AUXMAC_#", + .len = ( ETH_ALEN * 2 ), + .decode = acpimac_decode_base16, +}; + +/** "_RTXMAC_" extraction mechanism */ +static struct acpimac_extractor acpimac_rtxmac = { + .prefix = "_RTXMAC_#", + .len = ETH_ALEN, + .decode = acpimac_decode_raw, +}; /** * Extract MAC address from DSDT/SSDT @@ -59,6 +127,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * @v len Length of DSDT/SSDT * @v offset Offset of signature within DSDT/SSDT * @v data Data buffer + * @v extractor ACPI MAC address extractor * @ret rc Return status code * * Some vendors provide a "system MAC address" within the DSDT/SSDT, @@ -72,51 +141,44 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * string that appears shortly after an "AMAC" or "MACA" signature. * This should work for most implementations encountered in practice. */ -static int acpi_extract_mac ( userptr_t zsdt, size_t len, size_t offset, - void *data ) { - static const char prefix[9] = "_AUXMAC_#"; +static int acpimac_extract ( userptr_t zsdt, size_t len, size_t offset, + void *data, struct acpimac_extractor *extractor ){ + size_t prefix_len = strlen ( extractor->prefix ); uint8_t *hw_addr = data; size_t skip = 0; - char auxmac[ sizeof ( prefix ) /* "_AUXMAC_#" */ + - ( ETH_ALEN * 2 ) /* MAC */ + 1 /* "#" */ + 1 /* NUL */ ]; - char *mac = &auxmac[ sizeof ( prefix ) ]; - int decoded_len; + char buf[ prefix_len + extractor->len + 1 /* "#" */ + 1 /* NUL */ ]; + char *mac = &buf[prefix_len]; int rc; /* Skip signature and at least one tag byte */ offset += ( 4 /* signature */ + 1 /* tag byte */ ); - /* Scan for "_AUXMAC_#.....#" close to signature */ + /* Scan for suitable string close to signature */ for ( skip = 0 ; - ( ( skip < AUXMAC_MAX_SKIP ) && - ( offset + skip + sizeof ( auxmac ) ) < len ) ; + ( ( skip < ACPIMAC_MAX_SKIP ) && + ( offset + skip + sizeof ( buf ) ) <= len ) ; skip++ ) { /* Read value */ - copy_from_user ( auxmac, zsdt, ( offset + skip ), - sizeof ( auxmac ) ); + copy_from_user ( buf, zsdt, ( offset + skip ), + sizeof ( buf ) ); /* Check for expected format */ - if ( memcmp ( auxmac, prefix, sizeof ( prefix ) ) != 0 ) + if ( memcmp ( buf, extractor->prefix, prefix_len ) != 0 ) continue; - if ( auxmac[ sizeof ( auxmac ) - 2 ] != '#' ) + if ( buf[ sizeof ( buf ) - 2 ] != '#' ) continue; - if ( auxmac[ sizeof ( auxmac ) - 1 ] != '\0' ) + if ( buf[ sizeof ( buf ) - 1 ] != '\0' ) continue; - DBGC ( colour, "ACPI found MAC string \"%s\"\n", auxmac ); + DBGC ( colour, "ACPI found MAC:\n" ); + DBGC_HDA ( colour, ( offset + skip ), buf, sizeof ( buf ) ); /* Terminate MAC address string */ - mac = &auxmac[ sizeof ( prefix ) ]; - mac[ ETH_ALEN * 2 ] = '\0'; + mac[extractor->len] = '\0'; /* Decode MAC address */ - decoded_len = base16_decode ( mac, hw_addr, ETH_ALEN ); - if ( decoded_len < 0 ) { - rc = decoded_len; - DBGC ( colour, "ACPI could not decode MAC \"%s\": %s\n", - mac, strerror ( rc ) ); + if ( ( rc = extractor->decode ( mac, hw_addr ) ) != 0 ) return rc; - } /* Check MAC address validity */ if ( ! is_valid_ether_addr ( hw_addr ) ) { @@ -131,6 +193,36 @@ static int acpi_extract_mac ( userptr_t zsdt, size_t len, size_t offset, return -ENOENT; } +/** + * Extract "_AUXMAC_" MAC address from DSDT/SSDT + * + * @v zsdt DSDT or SSDT + * @v len Length of DSDT/SSDT + * @v offset Offset of signature within DSDT/SSDT + * @v data Data buffer + * @ret rc Return status code + */ +static int acpimac_extract_auxmac ( userptr_t zsdt, size_t len, size_t offset, + void *data ) { + + return acpimac_extract ( zsdt, len, offset, data, &acpimac_auxmac ); +} + +/** + * Extract "_RTXMAC_" MAC address from DSDT/SSDT + * + * @v zsdt DSDT or SSDT + * @v len Length of DSDT/SSDT + * @v offset Offset of signature within DSDT/SSDT + * @v data Data buffer + * @ret rc Return status code + */ +static int acpimac_extract_rtxmac ( userptr_t zsdt, size_t len, size_t offset, + void *data ) { + + return acpimac_extract ( zsdt, len, offset, data, &acpimac_rtxmac ); +} + /** * Extract MAC address from DSDT/SSDT * @@ -142,12 +234,17 @@ int acpi_mac ( uint8_t *hw_addr ) { /* Look for an "AMAC" address */ if ( ( rc = acpi_extract ( AMAC_SIGNATURE, hw_addr, - acpi_extract_mac ) ) == 0 ) + acpimac_extract_auxmac ) ) == 0 ) return 0; /* Look for a "MACA" address */ if ( ( rc = acpi_extract ( MACA_SIGNATURE, hw_addr, - acpi_extract_mac ) ) == 0 ) + acpimac_extract_auxmac ) ) == 0 ) + return 0; + + /* Look for a "RTMA" address */ + if ( ( rc = acpi_extract ( RTMA_SIGNATURE, hw_addr, + acpimac_extract_rtxmac ) ) == 0 ) return 0; return -ENOENT; diff --git a/src/tests/acpi_test.c b/src/tests/acpi_test.c index 972067ee2..1ca5befaf 100644 --- a/src/tests/acpi_test.c +++ b/src/tests/acpi_test.c @@ -159,6 +159,24 @@ ACPI_TABLES ( maca_tables, &maca_ssdt1, &maca_ssdt2 ); ACPI_MAC ( maca, &maca_tables, DATA ( 0x52, 0x54, 0x00, 0x11, 0x22, 0x33 ) ); +/** "RTMA" SSDT */ +ACPI_TABLE ( rtma_ssdt, "SSDT", + DATA ( 0x53, 0x53, 0x44, 0x54, 0x44, 0x00, 0x00, 0x00, 0x02, + 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x49, 0x4e, 0x54, 0x4c, 0x04, 0x06, 0x21, 0x20, + 0x10, 0x1f, 0x5c, 0x5f, 0x53, 0x42, 0x5f, 0x14, 0x18, + 0x52, 0x54, 0x4d, 0x41, 0x08, 0x0d, 0x5f, 0x52, 0x54, + 0x58, 0x4d, 0x41, 0x43, 0x5f, 0x23, 0x52, 0x54, 0x30, + 0x30, 0x30, 0x31, 0x23, 0x00 ) ); + +/** "RTMA" test tables */ +ACPI_TABLES ( rtma_tables, &rtma_ssdt ); + +/** "RTMA" test */ +ACPI_MAC ( rtma, &rtma_tables, + DATA ( 0x52, 0x54, 0x30, 0x30, 0x30, 0x31 ) ); + /** Current ACPI test table set */ static struct acpi_test_tables *acpi_test_tables; @@ -229,6 +247,7 @@ static void acpi_test_exec ( void ) { /* MAC extraction tests */ acpi_mac_ok ( &amac ); acpi_mac_ok ( &maca ); + acpi_mac_ok ( &rtma ); } /** ACPI self-test */ -- cgit v1.2.3-55-g7522 From 70995397e5bdfd3431e12971aa40630c7014785f Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 6 Apr 2022 14:36:07 +0100 Subject: [cloud] Allow aws-import script to run on Python 3.6 Signed-off-by: Michael Brown --- contrib/cloud/aws-import | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/cloud/aws-import b/contrib/cloud/aws-import index eef4302d5..b9a350f63 100755 --- a/contrib/cloud/aws-import +++ b/contrib/cloud/aws-import @@ -16,7 +16,7 @@ BLOCKSIZE = 512 * 1024 def detect_architecture(image): """Detect CPU architecture""" mdir = subprocess.run(['mdir', '-b', '-i', image, '::/EFI/BOOT'], - capture_output=True) + stdout=subprocess.PIPE, stderr=subprocess.PIPE) if any(b'BOOTAA64.EFI' in x for x in mdir.stdout.splitlines()): return 'arm64' return 'x86_64' -- cgit v1.2.3-55-g7522