From 9a96f55034e41b4e002b767e9218d55f03bdff7d Mon Sep 17 00:00:00 2001 From: Aishwarya Pant Date: Tue, 26 Sep 2017 13:58:49 +0530 Subject: drm: introduce drm_dev_{get/put} functions Reference counting functions in the kernel typically use get/put suffixes. For maintaining coding style consistency, introduce drm_dev_{get/put} functions. All callers of drm_dev_ref() API have been converted in this patch and hence it has been dropped while the drm_dev_unref() API with non-trivial number of users remains for compatibility. The semantic patch scripts/coccinelle/api/drm-get-put.cocci has been updated with the new helper for conversion of drm_dev_unref() to drm_dev_put() Signed-off-by: Aishwarya Pant Signed-off-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/6babda56134035a98220d5d37a4fd4048df214ce.1506413698.git.aishpant@gmail.com --- scripts/coccinelle/api/drm-get-put.cocci | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'scripts') diff --git a/scripts/coccinelle/api/drm-get-put.cocci b/scripts/coccinelle/api/drm-get-put.cocci index 0c7a9265c07e..dc10dee356c2 100644 --- a/scripts/coccinelle/api/drm-get-put.cocci +++ b/scripts/coccinelle/api/drm-get-put.cocci @@ -50,6 +50,9 @@ expression object; | - drm_property_unreference_blob(object) + drm_property_blob_put(object) +| +- drm_dev_unref(object) ++ drm_dev_put(object) ) @r depends on report@ @@ -81,6 +84,8 @@ drm_gem_object_unreference_unlocked(object) drm_property_unreference_blob@p(object) | drm_property_reference_blob@p(object) +| +drm_dev_unref@p(object) ) @script:python depends on report@ -- cgit v1.3-6-gb490 From d1ff70241a275133e1a0258b7c23588b122276c8 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 2 Oct 2017 13:38:34 +0300 Subject: thunderbolt: Add support for XDomain discovery protocol When two hosts are connected over a Thunderbolt cable, there is a protocol they can use to communicate capabilities supported by the host. The discovery protocol uses automatically configured control channel (ring 0) and is build on top of request/response transactions using special XDomain primitives provided by the Thunderbolt base protocol. The capabilities consists of a root directory block of basic properties used for identification of the host, and then there can be zero or more directories each describing a Thunderbolt service and its capabilities. Once both sides have discovered what is supported the two hosts can setup high-speed DMA paths and transfer data to the other side using whatever protocol was agreed based on the properties. The software protocol used to communicate which DMA paths to enable is service specific. This patch adds support for the XDomain discovery protocol to the Thunderbolt bus. We model each remote host connection as a Linux XDomain device. For each Thunderbolt service found supported on the XDomain device, we create Linux Thunderbolt service device which Thunderbolt service drivers can then bind to based on the protocol identification information retrieved from the property directory describing the service. This code is based on the work done by Amir Levy and Michael Jamet. Signed-off-by: Michael Jamet Signed-off-by: Mika Westerberg Reviewed-by: Yehezkel Bernat Reviewed-by: Andy Shevchenko Signed-off-by: David S. Miller --- Documentation/ABI/testing/sysfs-bus-thunderbolt | 48 + drivers/thunderbolt/Makefile | 2 +- drivers/thunderbolt/ctl.c | 11 +- drivers/thunderbolt/ctl.h | 2 +- drivers/thunderbolt/domain.c | 197 ++- drivers/thunderbolt/icm.c | 218 +++- drivers/thunderbolt/nhi.h | 2 + drivers/thunderbolt/switch.c | 7 +- drivers/thunderbolt/tb.h | 39 +- drivers/thunderbolt/tb_msgs.h | 123 ++ drivers/thunderbolt/xdomain.c | 1576 +++++++++++++++++++++++ include/linux/mod_devicetable.h | 26 + include/linux/thunderbolt.h | 242 ++++ scripts/mod/devicetable-offsets.c | 7 + scripts/mod/file2alias.c | 25 + 15 files changed, 2507 insertions(+), 18 deletions(-) create mode 100644 drivers/thunderbolt/xdomain.c (limited to 'scripts') diff --git a/Documentation/ABI/testing/sysfs-bus-thunderbolt b/Documentation/ABI/testing/sysfs-bus-thunderbolt index 392bef5bd399..93798c02e28b 100644 --- a/Documentation/ABI/testing/sysfs-bus-thunderbolt +++ b/Documentation/ABI/testing/sysfs-bus-thunderbolt @@ -110,3 +110,51 @@ Description: When new NVM image is written to the non-active NVM is directly the status value from the DMA configuration based mailbox before the device is power cycled. Writing 0 here clears the status. + +What: /sys/bus/thunderbolt/devices/./key +Date: Jan 2018 +KernelVersion: 4.15 +Contact: thunderbolt-software@lists.01.org +Description: This contains name of the property directory the XDomain + service exposes. This entry describes the protocol in + question. Following directories are already reserved by + the Apple XDomain specification: + + network: IP/ethernet over Thunderbolt + targetdm: Target disk mode protocol over Thunderbolt + extdisp: External display mode protocol over Thunderbolt + +What: /sys/bus/thunderbolt/devices/./modalias +Date: Jan 2018 +KernelVersion: 4.15 +Contact: thunderbolt-software@lists.01.org +Description: Stores the same MODALIAS value emitted by uevent for + the XDomain service. Format: tbtsvc:kSpNvNrN + +What: /sys/bus/thunderbolt/devices/./prtcid +Date: Jan 2018 +KernelVersion: 4.15 +Contact: thunderbolt-software@lists.01.org +Description: This contains XDomain protocol identifier the XDomain + service supports. + +What: /sys/bus/thunderbolt/devices/./prtcvers +Date: Jan 2018 +KernelVersion: 4.15 +Contact: thunderbolt-software@lists.01.org +Description: This contains XDomain protocol version the XDomain + service supports. + +What: /sys/bus/thunderbolt/devices/./prtcrevs +Date: Jan 2018 +KernelVersion: 4.15 +Contact: thunderbolt-software@lists.01.org +Description: This contains XDomain software version the XDomain + service supports. + +What: /sys/bus/thunderbolt/devices/./prtcstns +Date: Jan 2018 +KernelVersion: 4.15 +Contact: thunderbolt-software@lists.01.org +Description: This contains XDomain service specific settings as + bitmask. Format: %x diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile index 7afd21f5383a..f2f0de27252b 100644 --- a/drivers/thunderbolt/Makefile +++ b/drivers/thunderbolt/Makefile @@ -1,3 +1,3 @@ obj-${CONFIG_THUNDERBOLT} := thunderbolt.o thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o eeprom.o -thunderbolt-objs += domain.o dma_port.o icm.o property.o +thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index e6a4c9458c76..46e393c5fd1d 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -368,10 +368,10 @@ static int tb_ctl_tx(struct tb_ctl *ctl, const void *data, size_t len, /** * tb_ctl_handle_event() - acknowledge a plug event, invoke ctl->callback */ -static void tb_ctl_handle_event(struct tb_ctl *ctl, enum tb_cfg_pkg_type type, +static bool tb_ctl_handle_event(struct tb_ctl *ctl, enum tb_cfg_pkg_type type, struct ctl_pkg *pkg, size_t size) { - ctl->callback(ctl->callback_data, type, pkg->buffer, size); + return ctl->callback(ctl->callback_data, type, pkg->buffer, size); } static void tb_ctl_rx_submit(struct ctl_pkg *pkg) @@ -444,6 +444,8 @@ static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame, break; case TB_CFG_PKG_EVENT: + case TB_CFG_PKG_XDOMAIN_RESP: + case TB_CFG_PKG_XDOMAIN_REQ: if (*(__be32 *)(pkg->buffer + frame->size) != crc32) { tb_ctl_err(pkg->ctl, "RX: checksum mismatch, dropping packet\n"); @@ -451,8 +453,9 @@ static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame, } /* Fall through */ case TB_CFG_PKG_ICM_EVENT: - tb_ctl_handle_event(pkg->ctl, frame->eof, pkg, frame->size); - goto rx; + if (tb_ctl_handle_event(pkg->ctl, frame->eof, pkg, frame->size)) + goto rx; + break; default: break; diff --git a/drivers/thunderbolt/ctl.h b/drivers/thunderbolt/ctl.h index d0f21e1e0b8b..85c49dd301ea 100644 --- a/drivers/thunderbolt/ctl.h +++ b/drivers/thunderbolt/ctl.h @@ -16,7 +16,7 @@ /* control channel */ struct tb_ctl; -typedef void (*event_cb)(void *data, enum tb_cfg_pkg_type type, +typedef bool (*event_cb)(void *data, enum tb_cfg_pkg_type type, const void *buf, size_t size); struct tb_ctl *tb_ctl_alloc(struct tb_nhi *nhi, event_cb cb, void *cb_data); diff --git a/drivers/thunderbolt/domain.c b/drivers/thunderbolt/domain.c index 9f2dcd48974d..9b90115319ce 100644 --- a/drivers/thunderbolt/domain.c +++ b/drivers/thunderbolt/domain.c @@ -20,6 +20,98 @@ static DEFINE_IDA(tb_domain_ida); +static bool match_service_id(const struct tb_service_id *id, + const struct tb_service *svc) +{ + if (id->match_flags & TBSVC_MATCH_PROTOCOL_KEY) { + if (strcmp(id->protocol_key, svc->key)) + return false; + } + + if (id->match_flags & TBSVC_MATCH_PROTOCOL_ID) { + if (id->protocol_id != svc->prtcid) + return false; + } + + if (id->match_flags & TBSVC_MATCH_PROTOCOL_VERSION) { + if (id->protocol_version != svc->prtcvers) + return false; + } + + if (id->match_flags & TBSVC_MATCH_PROTOCOL_VERSION) { + if (id->protocol_revision != svc->prtcrevs) + return false; + } + + return true; +} + +static const struct tb_service_id *__tb_service_match(struct device *dev, + struct device_driver *drv) +{ + struct tb_service_driver *driver; + const struct tb_service_id *ids; + struct tb_service *svc; + + svc = tb_to_service(dev); + if (!svc) + return NULL; + + driver = container_of(drv, struct tb_service_driver, driver); + if (!driver->id_table) + return NULL; + + for (ids = driver->id_table; ids->match_flags != 0; ids++) { + if (match_service_id(ids, svc)) + return ids; + } + + return NULL; +} + +static int tb_service_match(struct device *dev, struct device_driver *drv) +{ + return !!__tb_service_match(dev, drv); +} + +static int tb_service_probe(struct device *dev) +{ + struct tb_service *svc = tb_to_service(dev); + struct tb_service_driver *driver; + const struct tb_service_id *id; + + driver = container_of(dev->driver, struct tb_service_driver, driver); + id = __tb_service_match(dev, &driver->driver); + + return driver->probe(svc, id); +} + +static int tb_service_remove(struct device *dev) +{ + struct tb_service *svc = tb_to_service(dev); + struct tb_service_driver *driver; + + driver = container_of(dev->driver, struct tb_service_driver, driver); + if (driver->remove) + driver->remove(svc); + + return 0; +} + +static void tb_service_shutdown(struct device *dev) +{ + struct tb_service_driver *driver; + struct tb_service *svc; + + svc = tb_to_service(dev); + if (!svc || !dev->driver) + return; + + driver = container_of(dev->driver, struct tb_service_driver, driver); + if (driver->shutdown) + driver->shutdown(svc); +} + static const char * const tb_security_names[] = { [TB_SECURITY_NONE] = "none", [TB_SECURITY_USER] = "user", @@ -52,6 +144,10 @@ static const struct attribute_group *domain_attr_groups[] = { struct bus_type tb_bus_type = { .name = "thunderbolt", + .match = tb_service_match, + .probe = tb_service_probe, + .remove = tb_service_remove, + .shutdown = tb_service_shutdown, }; static void tb_domain_release(struct device *dev) @@ -128,17 +224,26 @@ err_free: return NULL; } -static void tb_domain_event_cb(void *data, enum tb_cfg_pkg_type type, +static bool tb_domain_event_cb(void *data, enum tb_cfg_pkg_type type, const void *buf, size_t size) { struct tb *tb = data; if (!tb->cm_ops->handle_event) { tb_warn(tb, "domain does not have event handler\n"); - return; + return true; } - tb->cm_ops->handle_event(tb, type, buf, size); + switch (type) { + case TB_CFG_PKG_XDOMAIN_REQ: + case TB_CFG_PKG_XDOMAIN_RESP: + return tb_xdomain_handle_request(tb, type, buf, size); + + default: + tb->cm_ops->handle_event(tb, type, buf, size); + } + + return true; } /** @@ -443,9 +548,92 @@ int tb_domain_disconnect_pcie_paths(struct tb *tb) return tb->cm_ops->disconnect_pcie_paths(tb); } +/** + * tb_domain_approve_xdomain_paths() - Enable DMA paths for XDomain + * @tb: Domain enabling the DMA paths + * @xd: XDomain DMA paths are created to + * + * Calls connection manager specific method to enable DMA paths to the + * XDomain in question. + * + * Return: 0% in case of success and negative errno otherwise. In + * particular returns %-ENOTSUPP if the connection manager + * implementation does not support XDomains. + */ +int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) +{ + if (!tb->cm_ops->approve_xdomain_paths) + return -ENOTSUPP; + + return tb->cm_ops->approve_xdomain_paths(tb, xd); +} + +/** + * tb_domain_disconnect_xdomain_paths() - Disable DMA paths for XDomain + * @tb: Domain disabling the DMA paths + * @xd: XDomain whose DMA paths are disconnected + * + * Calls connection manager specific method to disconnect DMA paths to + * the XDomain in question. + * + * Return: 0% in case of success and negative errno otherwise. In + * particular returns %-ENOTSUPP if the connection manager + * implementation does not support XDomains. + */ +int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) +{ + if (!tb->cm_ops->disconnect_xdomain_paths) + return -ENOTSUPP; + + return tb->cm_ops->disconnect_xdomain_paths(tb, xd); +} + +static int disconnect_xdomain(struct device *dev, void *data) +{ + struct tb_xdomain *xd; + struct tb *tb = data; + int ret = 0; + + xd = tb_to_xdomain(dev); + if (xd && xd->tb == tb) + ret = tb_xdomain_disable_paths(xd); + + return ret; +} + +/** + * tb_domain_disconnect_all_paths() - Disconnect all paths for the domain + * @tb: Domain whose paths are disconnected + * + * This function can be used to disconnect all paths (PCIe, XDomain) for + * example in preparation for host NVM firmware upgrade. After this is + * called the paths cannot be established without resetting the switch. + * + * Return: %0 in case of success and negative errno otherwise. + */ +int tb_domain_disconnect_all_paths(struct tb *tb) +{ + int ret; + + ret = tb_domain_disconnect_pcie_paths(tb); + if (ret) + return ret; + + return bus_for_each_dev(&tb_bus_type, NULL, tb, disconnect_xdomain); +} + int tb_domain_init(void) { - return bus_register(&tb_bus_type); + int ret; + + ret = tb_xdomain_init(); + if (ret) + return ret; + ret = bus_register(&tb_bus_type); + if (ret) + tb_xdomain_exit(); + + return ret; } void tb_domain_exit(void) @@ -453,4 +641,5 @@ void tb_domain_exit(void) bus_unregister(&tb_bus_type); ida_destroy(&tb_domain_ida); tb_switch_exit(); + tb_xdomain_exit(); } diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index 8c22b91ed040..ab02d13f40b7 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -60,6 +60,8 @@ * @get_route: Find a route string for given switch * @device_connected: Handle device connected ICM message * @device_disconnected: Handle device disconnected ICM message + * @xdomain_connected - Handle XDomain connected ICM message + * @xdomain_disconnected - Handle XDomain disconnected ICM message */ struct icm { struct mutex request_lock; @@ -74,6 +76,10 @@ struct icm { const struct icm_pkg_header *hdr); void (*device_disconnected)(struct tb *tb, const struct icm_pkg_header *hdr); + void (*xdomain_connected)(struct tb *tb, + const struct icm_pkg_header *hdr); + void (*xdomain_disconnected)(struct tb *tb, + const struct icm_pkg_header *hdr); }; struct icm_notification { @@ -89,7 +95,10 @@ static inline struct tb *icm_to_tb(struct icm *icm) static inline u8 phy_port_from_route(u64 route, u8 depth) { - return tb_phy_port_from_link(route >> ((depth - 1) * 8)); + u8 link; + + link = depth ? route >> ((depth - 1) * 8) : route; + return tb_phy_port_from_link(link); } static inline u8 dual_link_from_link(u8 link) @@ -320,6 +329,51 @@ static int icm_fr_challenge_switch_key(struct tb *tb, struct tb_switch *sw, return 0; } +static int icm_fr_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) +{ + struct icm_fr_pkg_approve_xdomain_response reply; + struct icm_fr_pkg_approve_xdomain request; + int ret; + + memset(&request, 0, sizeof(request)); + request.hdr.code = ICM_APPROVE_XDOMAIN; + request.link_info = xd->depth << ICM_LINK_INFO_DEPTH_SHIFT | xd->link; + memcpy(&request.remote_uuid, xd->remote_uuid, sizeof(*xd->remote_uuid)); + + request.transmit_path = xd->transmit_path; + request.transmit_ring = xd->transmit_ring; + request.receive_path = xd->receive_path; + request.receive_ring = xd->receive_ring; + + memset(&reply, 0, sizeof(reply)); + ret = icm_request(tb, &request, sizeof(request), &reply, sizeof(reply), + 1, ICM_TIMEOUT); + if (ret) + return ret; + + if (reply.hdr.flags & ICM_FLAGS_ERROR) + return -EIO; + + return 0; +} + +static int icm_fr_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd) +{ + u8 phy_port; + u8 cmd; + + phy_port = tb_phy_port_from_link(xd->link); + if (phy_port == 0) + cmd = NHI_MAILBOX_DISCONNECT_PA; + else + cmd = NHI_MAILBOX_DISCONNECT_PB; + + nhi_mailbox_cmd(tb->nhi, cmd, 1); + usleep_range(10, 50); + nhi_mailbox_cmd(tb->nhi, cmd, 2); + return 0; +} + static void remove_switch(struct tb_switch *sw) { struct tb_switch *parent_sw; @@ -475,6 +529,141 @@ icm_fr_device_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) tb_switch_put(sw); } +static void remove_xdomain(struct tb_xdomain *xd) +{ + struct tb_switch *sw; + + sw = tb_to_switch(xd->dev.parent); + tb_port_at(xd->route, sw)->xdomain = NULL; + tb_xdomain_remove(xd); +} + +static void +icm_fr_xdomain_connected(struct tb *tb, const struct icm_pkg_header *hdr) +{ + const struct icm_fr_event_xdomain_connected *pkg = + (const struct icm_fr_event_xdomain_connected *)hdr; + struct tb_xdomain *xd; + struct tb_switch *sw; + u8 link, depth; + bool approved; + u64 route; + + /* + * After NVM upgrade adding root switch device fails because we + * initiated reset. During that time ICM might still send + * XDomain connected message which we ignore here. + */ + if (!tb->root_switch) + return; + + link = pkg->link_info & ICM_LINK_INFO_LINK_MASK; + depth = (pkg->link_info & ICM_LINK_INFO_DEPTH_MASK) >> + ICM_LINK_INFO_DEPTH_SHIFT; + approved = pkg->link_info & ICM_LINK_INFO_APPROVED; + + if (link > ICM_MAX_LINK || depth > ICM_MAX_DEPTH) { + tb_warn(tb, "invalid topology %u.%u, ignoring\n", link, depth); + return; + } + + route = get_route(pkg->local_route_hi, pkg->local_route_lo); + + xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); + if (xd) { + u8 xd_phy_port, phy_port; + + xd_phy_port = phy_port_from_route(xd->route, xd->depth); + phy_port = phy_port_from_route(route, depth); + + if (xd->depth == depth && xd_phy_port == phy_port) { + xd->link = link; + xd->route = route; + xd->is_unplugged = false; + tb_xdomain_put(xd); + return; + } + + /* + * If we find an existing XDomain connection remove it + * now. We need to go through login handshake and + * everything anyway to be able to re-establish the + * connection. + */ + remove_xdomain(xd); + tb_xdomain_put(xd); + } + + /* + * Look if there already exists an XDomain in the same place + * than the new one and in that case remove it because it is + * most likely another host that got disconnected. + */ + xd = tb_xdomain_find_by_link_depth(tb, link, depth); + if (!xd) { + u8 dual_link; + + dual_link = dual_link_from_link(link); + if (dual_link) + xd = tb_xdomain_find_by_link_depth(tb, dual_link, + depth); + } + if (xd) { + remove_xdomain(xd); + tb_xdomain_put(xd); + } + + /* + * If the user disconnected a switch during suspend and + * connected another host to the same port, remove the switch + * first. + */ + sw = get_switch_at_route(tb->root_switch, route); + if (sw) + remove_switch(sw); + + sw = tb_switch_find_by_link_depth(tb, link, depth); + if (!sw) { + tb_warn(tb, "no switch exists at %u.%u, ignoring\n", link, + depth); + return; + } + + xd = tb_xdomain_alloc(sw->tb, &sw->dev, route, + &pkg->local_uuid, &pkg->remote_uuid); + if (!xd) { + tb_switch_put(sw); + return; + } + + xd->link = link; + xd->depth = depth; + + tb_port_at(route, sw)->xdomain = xd; + + tb_xdomain_add(xd); + tb_switch_put(sw); +} + +static void +icm_fr_xdomain_disconnected(struct tb *tb, const struct icm_pkg_header *hdr) +{ + const struct icm_fr_event_xdomain_disconnected *pkg = + (const struct icm_fr_event_xdomain_disconnected *)hdr; + struct tb_xdomain *xd; + + /* + * If the connection is through one or multiple devices, the + * XDomain device is removed along with them so it is fine if we + * cannot find it here. + */ + xd = tb_xdomain_find_by_uuid(tb, &pkg->remote_uuid); + if (xd) { + remove_xdomain(xd); + tb_xdomain_put(xd); + } +} + static struct pci_dev *get_upstream_port(struct pci_dev *pdev) { struct pci_dev *parent; @@ -594,6 +783,12 @@ static void icm_handle_notification(struct work_struct *work) case ICM_EVENT_DEVICE_DISCONNECTED: icm->device_disconnected(tb, n->pkg); break; + case ICM_EVENT_XDOMAIN_CONNECTED: + icm->xdomain_connected(tb, n->pkg); + break; + case ICM_EVENT_XDOMAIN_DISCONNECTED: + icm->xdomain_disconnected(tb, n->pkg); + break; } mutex_unlock(&tb->lock); @@ -927,6 +1122,10 @@ static void icm_unplug_children(struct tb_switch *sw) if (tb_is_upstream_port(port)) continue; + if (port->xdomain) { + port->xdomain->is_unplugged = true; + continue; + } if (!port->remote) continue; @@ -943,6 +1142,13 @@ static void icm_free_unplugged_children(struct tb_switch *sw) if (tb_is_upstream_port(port)) continue; + + if (port->xdomain && port->xdomain->is_unplugged) { + tb_xdomain_remove(port->xdomain); + port->xdomain = NULL; + continue; + } + if (!port->remote) continue; @@ -1009,8 +1215,10 @@ static int icm_start(struct tb *tb) tb->root_switch->no_nvm_upgrade = x86_apple_machine; ret = tb_switch_add(tb->root_switch); - if (ret) + if (ret) { tb_switch_put(tb->root_switch); + tb->root_switch = NULL; + } return ret; } @@ -1042,6 +1250,8 @@ static const struct tb_cm_ops icm_fr_ops = { .add_switch_key = icm_fr_add_switch_key, .challenge_switch_key = icm_fr_challenge_switch_key, .disconnect_pcie_paths = icm_disconnect_pcie_paths, + .approve_xdomain_paths = icm_fr_approve_xdomain_paths, + .disconnect_xdomain_paths = icm_fr_disconnect_xdomain_paths, }; struct tb *icm_probe(struct tb_nhi *nhi) @@ -1064,6 +1274,8 @@ struct tb *icm_probe(struct tb_nhi *nhi) icm->get_route = icm_fr_get_route; icm->device_connected = icm_fr_device_connected; icm->device_disconnected = icm_fr_device_disconnected; + icm->xdomain_connected = icm_fr_xdomain_connected; + icm->xdomain_disconnected = icm_fr_xdomain_disconnected; tb->cm_ops = &icm_fr_ops; break; @@ -1077,6 +1289,8 @@ struct tb *icm_probe(struct tb_nhi *nhi) icm->get_route = icm_ar_get_route; icm->device_connected = icm_fr_device_connected; icm->device_disconnected = icm_fr_device_disconnected; + icm->xdomain_connected = icm_fr_xdomain_connected; + icm->xdomain_disconnected = icm_fr_xdomain_disconnected; tb->cm_ops = &icm_fr_ops; break; } diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h index 5b5bb2c436be..0e05828983db 100644 --- a/drivers/thunderbolt/nhi.h +++ b/drivers/thunderbolt/nhi.h @@ -157,6 +157,8 @@ enum nhi_mailbox_cmd { NHI_MAILBOX_SAVE_DEVS = 0x05, NHI_MAILBOX_DISCONNECT_PCIE_PATHS = 0x06, NHI_MAILBOX_DRV_UNLOADS = 0x07, + NHI_MAILBOX_DISCONNECT_PA = 0x10, + NHI_MAILBOX_DISCONNECT_PB = 0x11, NHI_MAILBOX_ALLOW_ALL_DEVS = 0x23, }; diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 53f40c57df59..dfc357d33e1e 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -171,11 +171,11 @@ static int nvm_authenticate_host(struct tb_switch *sw) /* * Root switch NVM upgrade requires that we disconnect the - * existing PCIe paths first (in case it is not in safe mode + * existing paths first (in case it is not in safe mode * already). */ if (!sw->safe_mode) { - ret = tb_domain_disconnect_pcie_paths(sw->tb); + ret = tb_domain_disconnect_all_paths(sw->tb); if (ret) return ret; /* @@ -1363,6 +1363,9 @@ void tb_switch_remove(struct tb_switch *sw) if (sw->ports[i].remote) tb_switch_remove(sw->ports[i].remote->sw); sw->ports[i].remote = NULL; + if (sw->ports[i].xdomain) + tb_xdomain_remove(sw->ports[i].xdomain); + sw->ports[i].xdomain = NULL; } if (!sw->is_unplugged) diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index ea21d927bd09..74af9d4929ab 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -9,6 +9,7 @@ #include #include +#include #include #include "tb_regs.h" @@ -109,14 +110,25 @@ struct tb_switch { /** * struct tb_port - a thunderbolt port, part of a tb_switch + * @config: Cached port configuration read from registers + * @sw: Switch the port belongs to + * @remote: Remote port (%NULL if not connected) + * @xdomain: Remote host (%NULL if not connected) + * @cap_phy: Offset, zero if not found + * @port: Port number on switch + * @disabled: Disabled by eeprom + * @dual_link_port: If the switch is connected using two ports, points + * to the other port. + * @link_nr: Is this primary or secondary port on the dual_link. */ struct tb_port { struct tb_regs_port_header config; struct tb_switch *sw; - struct tb_port *remote; /* remote port, NULL if not connected */ - int cap_phy; /* offset, zero if not found */ - u8 port; /* port number on switch */ - bool disabled; /* disabled by eeprom */ + struct tb_port *remote; + struct tb_xdomain *xdomain; + int cap_phy; + u8 port; + bool disabled; struct tb_port *dual_link_port; u8 link_nr:1; }; @@ -189,6 +201,8 @@ struct tb_path { * @add_switch_key: Add key to switch * @challenge_switch_key: Challenge switch using key * @disconnect_pcie_paths: Disconnects PCIe paths before NVM update + * @approve_xdomain_paths: Approve (establish) XDomain DMA paths + * @disconnect_xdomain_paths: Disconnect XDomain DMA paths */ struct tb_cm_ops { int (*driver_ready)(struct tb *tb); @@ -205,6 +219,8 @@ struct tb_cm_ops { int (*challenge_switch_key)(struct tb *tb, struct tb_switch *sw, const u8 *challenge, u8 *response); int (*disconnect_pcie_paths)(struct tb *tb); + int (*approve_xdomain_paths)(struct tb *tb, struct tb_xdomain *xd); + int (*disconnect_xdomain_paths)(struct tb *tb, struct tb_xdomain *xd); }; static inline void *tb_priv(struct tb *tb) @@ -331,6 +347,8 @@ extern struct device_type tb_switch_type; int tb_domain_init(void); void tb_domain_exit(void); void tb_switch_exit(void); +int tb_xdomain_init(void); +void tb_xdomain_exit(void); struct tb *tb_domain_alloc(struct tb_nhi *nhi, size_t privsize); int tb_domain_add(struct tb *tb); @@ -343,6 +361,9 @@ int tb_domain_approve_switch(struct tb *tb, struct tb_switch *sw); int tb_domain_approve_switch_key(struct tb *tb, struct tb_switch *sw); int tb_domain_challenge_switch_key(struct tb *tb, struct tb_switch *sw); int tb_domain_disconnect_pcie_paths(struct tb *tb); +int tb_domain_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd); +int tb_domain_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd); +int tb_domain_disconnect_all_paths(struct tb *tb); static inline void tb_domain_put(struct tb *tb) { @@ -422,4 +443,14 @@ static inline u64 tb_downstream_route(struct tb_port *port) | ((u64) port->port << (port->sw->config.depth * 8)); } +bool tb_xdomain_handle_request(struct tb *tb, enum tb_cfg_pkg_type type, + const void *buf, size_t size); +struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent, + u64 route, const uuid_t *local_uuid, + const uuid_t *remote_uuid); +void tb_xdomain_add(struct tb_xdomain *xd); +void tb_xdomain_remove(struct tb_xdomain *xd); +struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link, + u8 depth); + #endif diff --git a/drivers/thunderbolt/tb_msgs.h b/drivers/thunderbolt/tb_msgs.h index f2b2550cd97c..b0a092baa605 100644 --- a/drivers/thunderbolt/tb_msgs.h +++ b/drivers/thunderbolt/tb_msgs.h @@ -101,11 +101,14 @@ enum icm_pkg_code { ICM_CHALLENGE_DEVICE = 0x5, ICM_ADD_DEVICE_KEY = 0x6, ICM_GET_ROUTE = 0xa, + ICM_APPROVE_XDOMAIN = 0x10, }; enum icm_event_code { ICM_EVENT_DEVICE_CONNECTED = 3, ICM_EVENT_DEVICE_DISCONNECTED = 4, + ICM_EVENT_XDOMAIN_CONNECTED = 6, + ICM_EVENT_XDOMAIN_DISCONNECTED = 7, }; struct icm_pkg_header { @@ -188,6 +191,25 @@ struct icm_fr_event_device_disconnected { u16 link_info; }; +struct icm_fr_event_xdomain_connected { + struct icm_pkg_header hdr; + u16 reserved; + u16 link_info; + uuid_t remote_uuid; + uuid_t local_uuid; + u32 local_route_hi; + u32 local_route_lo; + u32 remote_route_hi; + u32 remote_route_lo; +}; + +struct icm_fr_event_xdomain_disconnected { + struct icm_pkg_header hdr; + u16 reserved; + u16 link_info; + uuid_t remote_uuid; +}; + struct icm_fr_pkg_add_device_key { struct icm_pkg_header hdr; uuid_t ep_uuid; @@ -224,6 +246,28 @@ struct icm_fr_pkg_challenge_device_response { u32 response[8]; }; +struct icm_fr_pkg_approve_xdomain { + struct icm_pkg_header hdr; + u16 reserved; + u16 link_info; + uuid_t remote_uuid; + u16 transmit_path; + u16 transmit_ring; + u16 receive_path; + u16 receive_ring; +}; + +struct icm_fr_pkg_approve_xdomain_response { + struct icm_pkg_header hdr; + u16 reserved; + u16 link_info; + uuid_t remote_uuid; + u16 transmit_path; + u16 transmit_ring; + u16 receive_path; + u16 receive_ring; +}; + /* Alpine Ridge only messages */ struct icm_ar_pkg_get_route { @@ -240,4 +284,83 @@ struct icm_ar_pkg_get_route_response { u32 route_lo; }; +/* XDomain messages */ + +struct tb_xdomain_header { + u32 route_hi; + u32 route_lo; + u32 length_sn; +}; + +#define TB_XDOMAIN_LENGTH_MASK GENMASK(5, 0) +#define TB_XDOMAIN_SN_MASK GENMASK(28, 27) +#define TB_XDOMAIN_SN_SHIFT 27 + +enum tb_xdp_type { + UUID_REQUEST_OLD = 1, + UUID_RESPONSE = 2, + PROPERTIES_REQUEST, + PROPERTIES_RESPONSE, + PROPERTIES_CHANGED_REQUEST, + PROPERTIES_CHANGED_RESPONSE, + ERROR_RESPONSE, + UUID_REQUEST = 12, +}; + +struct tb_xdp_header { + struct tb_xdomain_header xd_hdr; + uuid_t uuid; + u32 type; +}; + +struct tb_xdp_properties { + struct tb_xdp_header hdr; + uuid_t src_uuid; + uuid_t dst_uuid; + u16 offset; + u16 reserved; +}; + +struct tb_xdp_properties_response { + struct tb_xdp_header hdr; + uuid_t src_uuid; + uuid_t dst_uuid; + u16 offset; + u16 data_length; + u32 generation; + u32 data[0]; +}; + +/* + * Max length of data array single XDomain property response is allowed + * to carry. + */ +#define TB_XDP_PROPERTIES_MAX_DATA_LENGTH \ + (((256 - 4 - sizeof(struct tb_xdp_properties_response))) / 4) + +/* Maximum size of the total property block in dwords we allow */ +#define TB_XDP_PROPERTIES_MAX_LENGTH 500 + +struct tb_xdp_properties_changed { + struct tb_xdp_header hdr; + uuid_t src_uuid; +}; + +struct tb_xdp_properties_changed_response { + struct tb_xdp_header hdr; +}; + +enum tb_xdp_error { + ERROR_SUCCESS, + ERROR_UNKNOWN_PACKET, + ERROR_UNKNOWN_DOMAIN, + ERROR_NOT_SUPPORTED, + ERROR_NOT_READY, +}; + +struct tb_xdp_error_response { + struct tb_xdp_header hdr; + u32 error; +}; + #endif diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c new file mode 100644 index 000000000000..f2d06f6f7be9 --- /dev/null +++ b/drivers/thunderbolt/xdomain.c @@ -0,0 +1,1576 @@ +/* + * Thunderbolt XDomain discovery protocol support + * + * Copyright (C) 2017, Intel Corporation + * Authors: Michael Jamet + * Mika Westerberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include "tb.h" + +#define XDOMAIN_DEFAULT_TIMEOUT 5000 /* ms */ +#define XDOMAIN_PROPERTIES_RETRIES 60 +#define XDOMAIN_PROPERTIES_CHANGED_RETRIES 10 + +struct xdomain_request_work { + struct work_struct work; + struct tb_xdp_header *pkg; + struct tb *tb; +}; + +/* Serializes access to the properties and protocol handlers below */ +static DEFINE_MUTEX(xdomain_lock); + +/* Properties exposed to the remote domains */ +static struct tb_property_dir *xdomain_property_dir; +static u32 *xdomain_property_block; +static u32 xdomain_property_block_len; +static u32 xdomain_property_block_gen; + +/* Additional protocol handlers */ +static LIST_HEAD(protocol_handlers); + +/* UUID for XDomain discovery protocol: b638d70e-42ff-40bb-97c2-90e2c0b2ff07 */ +static const uuid_t tb_xdp_uuid = + UUID_INIT(0xb638d70e, 0x42ff, 0x40bb, + 0x97, 0xc2, 0x90, 0xe2, 0xc0, 0xb2, 0xff, 0x07); + +static bool tb_xdomain_match(const struct tb_cfg_request *req, + const struct ctl_pkg *pkg) +{ + switch (pkg->frame.eof) { + case TB_CFG_PKG_ERROR: + return true; + + case TB_CFG_PKG_XDOMAIN_RESP: { + const struct tb_xdp_header *res_hdr = pkg->buffer; + const struct tb_xdp_header *req_hdr = req->request; + u8 req_seq, res_seq; + + if (pkg->frame.size < req->response_size / 4) + return false; + + /* Make sure route matches */ + if ((res_hdr->xd_hdr.route_hi & ~BIT(31)) != + req_hdr->xd_hdr.route_hi) + return false; + if ((res_hdr->xd_hdr.route_lo) != req_hdr->xd_hdr.route_lo) + return false; + + /* Then check that the sequence number matches */ + res_seq = res_hdr->xd_hdr.length_sn & TB_XDOMAIN_SN_MASK; + res_seq >>= TB_XDOMAIN_SN_SHIFT; + req_seq = req_hdr->xd_hdr.length_sn & TB_XDOMAIN_SN_MASK; + req_seq >>= TB_XDOMAIN_SN_SHIFT; + if (res_seq != req_seq) + return false; + + /* Check that the XDomain protocol matches */ + if (!uuid_equal(&res_hdr->uuid, &req_hdr->uuid)) + return false; + + return true; + } + + default: + return false; + } +} + +static bool tb_xdomain_copy(struct tb_cfg_request *req, + const struct ctl_pkg *pkg) +{ + memcpy(req->response, pkg->buffer, req->response_size); + req->result.err = 0; + return true; +} + +static void response_ready(void *data) +{ + tb_cfg_request_put(data); +} + +static int __tb_xdomain_response(struct tb_ctl *ctl, const void *response, + size_t size, enum tb_cfg_pkg_type type) +{ + struct tb_cfg_request *req; + + req = tb_cfg_request_alloc(); + if (!req) + return -ENOMEM; + + req->match = tb_xdomain_match; + req->copy = tb_xdomain_copy; + req->request = response; + req->request_size = size; + req->request_type = type; + + return tb_cfg_request(ctl, req, response_ready, req); +} + +/** + * tb_xdomain_response() - Send a XDomain response message + * @xd: XDomain to send the message + * @response: Response to send + * @size: Size of the response + * @type: PDF type of the response + * + * This can be used to send a XDomain response message to the other + * domain. No response for the message is expected. + * + * Return: %0 in case of success and negative errno in case of failure + */ +int tb_xdomain_response(struct tb_xdomain *xd, const void *response, + size_t size, enum tb_cfg_pkg_type type) +{ + return __tb_xdomain_response(xd->tb->ctl, response, size, type); +} +EXPORT_SYMBOL_GPL(tb_xdomain_response); + +static int __tb_xdomain_request(struct tb_ctl *ctl, const void *request, + size_t request_size, enum tb_cfg_pkg_type request_type, void *response, + size_t response_size, enum tb_cfg_pkg_type response_type, + unsigned int timeout_msec) +{ + struct tb_cfg_request *req; + struct tb_cfg_result res; + + req = tb_cfg_request_alloc(); + if (!req) + return -ENOMEM; + + req->match = tb_xdomain_match; + req->copy = tb_xdomain_copy; + req->request = request; + req->request_size = request_size; + req->request_type = request_type; + req->response = response; + req->response_size = response_size; + req->response_type = response_type; + + res = tb_cfg_request_sync(ctl, req, timeout_msec); + + tb_cfg_request_put(req); + + return res.err == 1 ? -EIO : res.err; +} + +/** + * tb_xdomain_request() - Send a XDomain request + * @xd: XDomain to send the request + * @request: Request to send + * @request_size: Size of the request in bytes + * @request_type: PDF type of the request + * @response: Response is copied here + * @response_size: Expected size of the response in bytes + * @response_type: Expected PDF type of the response + * @timeout_msec: Timeout in milliseconds to wait for the response + * + * This function can be used to send XDomain control channel messages to + * the other domain. The function waits until the response is received + * or when timeout triggers. Whichever comes first. + * + * Return: %0 in case of success and negative errno in case of failure + */ +int tb_xdomain_request(struct tb_xdomain *xd, const void *request, + size_t request_size, enum tb_cfg_pkg_type request_type, + void *response, size_t response_size, + enum tb_cfg_pkg_type response_type, unsigned int timeout_msec) +{ + return __tb_xdomain_request(xd->tb->ctl, request, request_size, + request_type, response, response_size, + response_type, timeout_msec); +} +EXPORT_SYMBOL_GPL(tb_xdomain_request); + +static inline void tb_xdp_fill_header(struct tb_xdp_header *hdr, u64 route, + u8 sequence, enum tb_xdp_type type, size_t size) +{ + u32 length_sn; + + length_sn = (size - sizeof(hdr->xd_hdr)) / 4; + length_sn |= (sequence << TB_XDOMAIN_SN_SHIFT) & TB_XDOMAIN_SN_MASK; + + hdr->xd_hdr.route_hi = upper_32_bits(route); + hdr->xd_hdr.route_lo = lower_32_bits(route); + hdr->xd_hdr.length_sn = length_sn; + hdr->type = type; + memcpy(&hdr->uuid, &tb_xdp_uuid, sizeof(tb_xdp_uuid)); +} + +static int tb_xdp_handle_error(const struct tb_xdp_header *hdr) +{ + const struct tb_xdp_error_response *error; + + if (hdr->type != ERROR_RESPONSE) + return 0; + + error = (const struct tb_xdp_error_response *)hdr; + + switch (error->error) { + case ERROR_UNKNOWN_PACKET: + case ERROR_UNKNOWN_DOMAIN: + return -EIO; + case ERROR_NOT_SUPPORTED: + return -ENOTSUPP; + case ERROR_NOT_READY: + return -EAGAIN; + default: + break; + } + + return 0; +} + +static int tb_xdp_error_response(struct tb_ctl *ctl, u64 route, u8 sequence, + enum tb_xdp_error error) +{ + struct tb_xdp_error_response res; + + memset(&res, 0, sizeof(res)); + tb_xdp_fill_header(&res.hdr, route, sequence, ERROR_RESPONSE, + sizeof(res)); + res.error = error; + + return __tb_xdomain_response(ctl, &res, sizeof(res), + TB_CFG_PKG_XDOMAIN_RESP); +} + +static int tb_xdp_properties_request(struct tb_ctl *ctl, u64 route, + const uuid_t *src_uuid, const uuid_t *dst_uuid, int retry, + u32 **block, u32 *generation) +{ + struct tb_xdp_properties_response *res; + struct tb_xdp_properties req; + u16 data_len, len; + size_t total_size; + u32 *data = NULL; + int ret; + + total_size = sizeof(*res) + TB_XDP_PROPERTIES_MAX_DATA_LENGTH * 4; + res = kzalloc(total_size, GFP_KERNEL); + if (!res) + return -ENOMEM; + + memset(&req, 0, sizeof(req)); + tb_xdp_fill_header(&req.hdr, route, retry % 4, PROPERTIES_REQUEST, + sizeof(req)); + memcpy(&req.src_uuid, src_uuid, sizeof(*src_uuid)); + memcpy(&req.dst_uuid, dst_uuid, sizeof(*dst_uuid)); + + len = 0; + data_len = 0; + + do { + ret = __tb_xdomain_request(ctl, &req, sizeof(req), + TB_CFG_PKG_XDOMAIN_REQ, res, + total_size, TB_CFG_PKG_XDOMAIN_RESP, + XDOMAIN_DEFAULT_TIMEOUT); + if (ret) + goto err; + + ret = tb_xdp_handle_error(&res->hdr); + if (ret) + goto err; + + /* + * Package length includes the whole payload without the + * XDomain header. Validate first that the package is at + * least size of the response structure. + */ + len = res->hdr.xd_hdr.length_sn & TB_XDOMAIN_LENGTH_MASK; + if (len < sizeof(*res) / 4) { + ret = -EINVAL; + goto err; + } + + len += sizeof(res->hdr.xd_hdr) / 4; + len -= sizeof(*res) / 4; + + if (res->offset != req.offset) { + ret = -EINVAL; + goto err; + } + + /* + * First time allocate block that has enough space for + * the whole properties block. + */ + if (!data) { + data_len = res->data_length; + if (data_len > TB_XDP_PROPERTIES_MAX_LENGTH) { + ret = -E2BIG; + goto err; + } + + data = kcalloc(data_len, sizeof(u32), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto err; + } + } + + memcpy(data + req.offset, res->data, len * 4); + req.offset += len; + } while (!data_len || req.offset < data_len); + + *block = data; + *generation = res->generation; + + kfree(res); + + return data_len; + +err: + kfree(data); + kfree(res); + + return ret; +} + +static int tb_xdp_properties_response(struct tb *tb, struct tb_ctl *ctl, + u64 route, u8 sequence, const uuid_t *src_uuid, + const struct tb_xdp_properties *req) +{ + struct tb_xdp_properties_response *res; + size_t total_size; + u16 len; + int ret; + + /* + * Currently we expect all requests to be directed to us. The + * protocol supports forwarding, though which we might add + * support later on. + */ + if (!uuid_equal(src_uuid, &req->dst_uuid)) { + tb_xdp_error_response(ctl, route, sequence, + ERROR_UNKNOWN_DOMAIN); + return 0; + } + + mutex_lock(&xdomain_lock); + + if (req->offset >= xdomain_property_block_len) { + mutex_unlock(&xdomain_lock); + return -EINVAL; + } + + len = xdomain_property_block_len - req->offset; + len = min_t(u16, len, TB_XDP_PROPERTIES_MAX_DATA_LENGTH); + total_size = sizeof(*res) + len * 4; + + res = kzalloc(total_size, GFP_KERNEL); + if (!res) { + mutex_unlock(&xdomain_lock); + return -ENOMEM; + } + + tb_xdp_fill_header(&res->hdr, route, sequence, PROPERTIES_RESPONSE, + total_size); + res->generation = xdomain_property_block_gen; + res->data_length = xdomain_property_block_len; + res->offset = req->offset; + uuid_copy(&res->src_uuid, src_uuid); + uuid_copy(&res->dst_uuid, &req->src_uuid); + memcpy(res->data, &xdomain_property_block[req->offset], len * 4); + + mutex_unlock(&xdomain_lock); + + ret = __tb_xdomain_response(ctl, res, total_size, + TB_CFG_PKG_XDOMAIN_RESP); + + kfree(res); + return ret; +} + +static int tb_xdp_properties_changed_request(struct tb_ctl *ctl, u64 route, + int retry, const uuid_t *uuid) +{ + struct tb_xdp_properties_changed_response res; + struct tb_xdp_properties_changed req; + int ret; + + memset(&req, 0, sizeof(req)); + tb_xdp_fill_header(&req.hdr, route, retry % 4, + PROPERTIES_CHANGED_REQUEST, sizeof(req)); + uuid_copy(&req.src_uuid, uuid); + + memset(&res, 0, sizeof(res)); + ret = __tb_xdomain_request(ctl, &req, sizeof(req), + TB_CFG_PKG_XDOMAIN_REQ, &res, sizeof(res), + TB_CFG_PKG_XDOMAIN_RESP, + XDOMAIN_DEFAULT_TIMEOUT); + if (ret) + return ret; + + return tb_xdp_handle_error(&res.hdr); +} + +static int +tb_xdp_properties_changed_response(struct tb_ctl *ctl, u64 route, u8 sequence) +{ + struct tb_xdp_properties_changed_response res; + + memset(&res, 0, sizeof(res)); + tb_xdp_fill_header(&res.hdr, route, sequence, + PROPERTIES_CHANGED_RESPONSE, sizeof(res)); + return __tb_xdomain_response(ctl, &res, sizeof(res), + TB_CFG_PKG_XDOMAIN_RESP); +} + +/** + * tb_register_protocol_handler() - Register protocol handler + * @handler: Handler to register + * + * This allows XDomain service drivers to hook into incoming XDomain + * messages. After this function is called the service driver needs to + * be able to handle calls to callback whenever a package with the + * registered protocol is received. + */ +int tb_register_protocol_handler(struct tb_protocol_handler *handler) +{ + if (!handler->uuid || !handler->callback) + return -EINVAL; + if (uuid_equal(handler->uuid, &tb_xdp_uuid)) + return -EINVAL; + + mutex_lock(&xdomain_lock); + list_add_tail(&handler->list, &protocol_handlers); + mutex_unlock(&xdomain_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(tb_register_protocol_handler); + +/** + * tb_unregister_protocol_handler() - Unregister protocol handler + * @handler: Handler to unregister + * + * Removes the previously registered protocol handler. + */ +void tb_unregister_protocol_handler(struct tb_protocol_handler *handler) +{ + mutex_lock(&xdomain_lock); + list_del_init(&handler->list); + mutex_unlock(&xdomain_lock); +} +EXPORT_SYMBOL_GPL(tb_unregister_protocol_handler); + +static void tb_xdp_handle_request(struct work_struct *work) +{ + struct xdomain_request_work *xw = container_of(work, typeof(*xw), work); + const struct tb_xdp_header *pkg = xw->pkg; + const struct tb_xdomain_header *xhdr = &pkg->xd_hdr; + struct tb *tb = xw->tb; + struct tb_ctl *ctl = tb->ctl; + const uuid_t *uuid; + int ret = 0; + u8 sequence; + u64 route; + + route = ((u64)xhdr->route_hi << 32 | xhdr->route_lo) & ~BIT_ULL(63); + sequence = xhdr->length_sn & TB_XDOMAIN_SN_MASK; + sequence >>= TB_XDOMAIN_SN_SHIFT; + + mutex_lock(&tb->lock); + if (tb->root_switch) + uuid = tb->root_switch->uuid; + else + uuid = NULL; + mutex_unlock(&tb->lock); + + if (!uuid) { + tb_xdp_error_response(ctl, route, sequence, ERROR_NOT_READY); + goto out; + } + + switch (pkg->type) { + case PROPERTIES_REQUEST: + ret = tb_xdp_properties_response(tb, ctl, route, sequence, uuid, + (const struct tb_xdp_properties *)pkg); + break; + + case PROPERTIES_CHANGED_REQUEST: { + const struct tb_xdp_properties_changed *xchg = + (const struct tb_xdp_properties_changed *)pkg; + struct tb_xdomain *xd; + + ret = tb_xdp_properties_changed_response(ctl, route, sequence); + + /* + * Since the properties have been changed, let's update + * the xdomain related to this connection as well in + * case there is a change in services it offers. + */ + xd = tb_xdomain_find_by_uuid_locked(tb, &xchg->src_uuid); + if (xd) { + queue_delayed_work(tb->wq, &xd->get_properties_work, + msecs_to_jiffies(50)); + tb_xdomain_put(xd); + } + + break; + } + + default: + break; + } + + if (ret) { + tb_warn(tb, "failed to send XDomain response for %#x\n", + pkg->type); + } + +out: + kfree(xw->pkg); + kfree(xw); +} + +static void +tb_xdp_schedule_request(struct tb *tb, const struct tb_xdp_header *hdr, + size_t size) +{ + struct xdomain_request_work *xw; + + xw = kmalloc(sizeof(*xw), GFP_KERNEL); + if (!xw) + return; + + INIT_WORK(&xw->work, tb_xdp_handle_request); + xw->pkg = kmemdup(hdr, size, GFP_KERNEL); + xw->tb = tb; + + queue_work(tb->wq, &xw->work); +} + +/** + * tb_register_service_driver() - Register XDomain service driver + * @drv: Driver to register + * + * Registers new service driver from @drv to the bus. + */ +int tb_register_service_driver(struct tb_service_driver *drv) +{ + drv->driver.bus = &tb_bus_type; + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(tb_register_service_driver); + +/** + * tb_unregister_service_driver() - Unregister XDomain service driver + * @xdrv: Driver to unregister + * + * Unregisters XDomain service driver from the bus. + */ +void tb_unregister_service_driver(struct tb_service_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(tb_unregister_service_driver); + +static ssize_t key_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_service *svc = container_of(dev, struct tb_service, dev); + + /* + * It should be null terminated but anything else is pretty much + * allowed. + */ + return sprintf(buf, "%*pEp\n", (int)strlen(svc->key), svc->key); +} +static DEVICE_ATTR_RO(key); + +static int get_modalias(struct tb_service *svc, char *buf, size_t size) +{ + return snprintf(buf, size, "tbsvc:k%sp%08Xv%08Xr%08X", svc->key, + svc->prtcid, svc->prtcvers, svc->prtcrevs); +} + +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_service *svc = container_of(dev, struct tb_service, dev); + + /* Full buffer size except new line and null termination */ + get_modalias(svc, buf, PAGE_SIZE - 2); + return sprintf(buf, "%s\n", buf); +} +static DEVICE_ATTR_RO(modalias); + +static ssize_t prtcid_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_service *svc = container_of(dev, struct tb_service, dev); + + return sprintf(buf, "%u\n", svc->prtcid); +} +static DEVICE_ATTR_RO(prtcid); + +static ssize_t prtcvers_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_service *svc = container_of(dev, struct tb_service, dev); + + return sprintf(buf, "%u\n", svc->prtcvers); +} +static DEVICE_ATTR_RO(prtcvers); + +static ssize_t prtcrevs_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_service *svc = container_of(dev, struct tb_service, dev); + + return sprintf(buf, "%u\n", svc->prtcrevs); +} +static DEVICE_ATTR_RO(prtcrevs); + +static ssize_t prtcstns_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_service *svc = container_of(dev, struct tb_service, dev); + + return sprintf(buf, "0x%08x\n", svc->prtcstns); +} +static DEVICE_ATTR_RO(prtcstns); + +static struct attribute *tb_service_attrs[] = { + &dev_attr_key.attr, + &dev_attr_modalias.attr, + &dev_attr_prtcid.attr, + &dev_attr_prtcvers.attr, + &dev_attr_prtcrevs.attr, + &dev_attr_prtcstns.attr, + NULL, +}; + +static struct attribute_group tb_service_attr_group = { + .attrs = tb_service_attrs, +}; + +static const struct attribute_group *tb_service_attr_groups[] = { + &tb_service_attr_group, + NULL, +}; + +static int tb_service_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct tb_service *svc = container_of(dev, struct tb_service, dev); + char modalias[64]; + + get_modalias(svc, modalias, sizeof(modalias)); + return add_uevent_var(env, "MODALIAS=%s", modalias); +} + +static void tb_service_release(struct device *dev) +{ + struct tb_service *svc = container_of(dev, struct tb_service, dev); + struct tb_xdomain *xd = tb_service_parent(svc); + + ida_simple_remove(&xd->service_ids, svc->id); + kfree(svc->key); + kfree(svc); +} + +struct device_type tb_service_type = { + .name = "thunderbolt_service", + .groups = tb_service_attr_groups, + .uevent = tb_service_uevent, + .release = tb_service_release, +}; +EXPORT_SYMBOL_GPL(tb_service_type); + +static int remove_missing_service(struct device *dev, void *data) +{ + struct tb_xdomain *xd = data; + struct tb_service *svc; + + svc = tb_to_service(dev); + if (!svc) + return 0; + + if (!tb_property_find(xd->properties, svc->key, + TB_PROPERTY_TYPE_DIRECTORY)) + device_unregister(dev); + + return 0; +} + +static int find_service(struct device *dev, void *data) +{ + const struct tb_property *p = data; + struct tb_service *svc; + + svc = tb_to_service(dev); + if (!svc) + return 0; + + return !strcmp(svc->key, p->key); +} + +static int populate_service(struct tb_service *svc, + struct tb_property *property) +{ + struct tb_property_dir *dir = property->value.dir; + struct tb_property *p; + + /* Fill in standard properties */ + p = tb_property_find(dir, "prtcid", TB_PROPERTY_TYPE_VALUE); + if (p) + svc->prtcid = p->value.immediate; + p = tb_property_find(dir, "prtcvers", TB_PROPERTY_TYPE_VALUE); + if (p) + svc->prtcvers = p->value.immediate; + p = tb_property_find(dir, "prtcrevs", TB_PROPERTY_TYPE_VALUE); + if (p) + svc->prtcrevs = p->value.immediate; + p = tb_property_find(dir, "prtcstns", TB_PROPERTY_TYPE_VALUE); + if (p) + svc->prtcstns = p->value.immediate; + + svc->key = kstrdup(property->key, GFP_KERNEL); + if (!svc->key) + return -ENOMEM; + + return 0; +} + +static void enumerate_services(struct tb_xdomain *xd) +{ + struct tb_service *svc; + struct tb_property *p; + struct device *dev; + + /* + * First remove all services that are not available anymore in + * the updated property block. + */ + device_for_each_child_reverse(&xd->dev, xd, remove_missing_service); + + /* Then re-enumerate properties creating new services as we go */ + tb_property_for_each(xd->properties, p) { + if (p->type != TB_PROPERTY_TYPE_DIRECTORY) + continue; + + /* If the service exists already we are fine */ + dev = device_find_child(&xd->dev, p, find_service); + if (dev) { + put_device(dev); + continue; + } + + svc = kzalloc(sizeof(*svc), GFP_KERNEL); + if (!svc) + break; + + if (populate_service(svc, p)) { + kfree(svc); + break; + } + + svc->id = ida_simple_get(&xd->service_ids, 0, 0, GFP_KERNEL); + svc->dev.bus = &tb_bus_type; + svc->dev.type = &tb_service_type; + svc->dev.parent = &xd->dev; + dev_set_name(&svc->dev, "%s.%d", dev_name(&xd->dev), svc->id); + + if (device_register(&svc->dev)) { + put_device(&svc->dev); + break; + } + } +} + +static int populate_properties(struct tb_xdomain *xd, + struct tb_property_dir *dir) +{ + const struct tb_property *p; + + /* Required properties */ + p = tb_property_find(dir, "deviceid", TB_PROPERTY_TYPE_VALUE); + if (!p) + return -EINVAL; + xd->device = p->value.immediate; + + p = tb_property_find(dir, "vendorid", TB_PROPERTY_TYPE_VALUE); + if (!p) + return -EINVAL; + xd->vendor = p->value.immediate; + + kfree(xd->device_name); + xd->device_name = NULL; + kfree(xd->vendor_name); + xd->vendor_name = NULL; + + /* Optional properties */ + p = tb_property_find(dir, "deviceid", TB_PROPERTY_TYPE_TEXT); + if (p) + xd->device_name = kstrdup(p->value.text, GFP_KERNEL); + p = tb_property_find(dir, "vendorid", TB_PROPERTY_TYPE_TEXT); + if (p) + xd->vendor_name = kstrdup(p->value.text, GFP_KERNEL); + + return 0; +} + +/* Called with @xd->lock held */ +static void tb_xdomain_restore_paths(struct tb_xdomain *xd) +{ + if (!xd->resume) + return; + + xd->resume = false; + if (xd->transmit_path) { + dev_dbg(&xd->dev, "re-establishing DMA path\n"); + tb_domain_approve_xdomain_paths(xd->tb, xd); + } +} + +static void tb_xdomain_get_properties(struct work_struct *work) +{ + struct tb_xdomain *xd = container_of(work, typeof(*xd), + get_properties_work.work); + struct tb_property_dir *dir; + struct tb *tb = xd->tb; + bool update = false; + u32 *block = NULL; + u32 gen = 0; + int ret; + + ret = tb_xdp_properties_request(tb->ctl, xd->route, xd->local_uuid, + xd->remote_uuid, xd->properties_retries, + &block, &gen); + if (ret < 0) { + if (xd->properties_retries-- > 0) { + queue_delayed_work(xd->tb->wq, &xd->get_properties_work, + msecs_to_jiffies(1000)); + } else { + /* Give up now */ + dev_err(&xd->dev, + "failed read XDomain properties from %pUb\n", + xd->remote_uuid); + } + return; + } + + xd->properties_retries = XDOMAIN_PROPERTIES_RETRIES; + + mutex_lock(&xd->lock); + + /* Only accept newer generation properties */ + if (xd->properties && gen <= xd->property_block_gen) { + /* + * On resume it is likely that the properties block is + * not changed (unless the other end added or removed + * services). However, we need to make sure the existing + * DMA paths are restored properly. + */ + tb_xdomain_restore_paths(xd); + goto err_free_block; + } + + dir = tb_property_parse_dir(block, ret); + if (!dir) { + dev_err(&xd->dev, "failed to parse XDomain properties\n"); + goto err_free_block; + } + + ret = populate_properties(xd, dir); + if (ret) { + dev_err(&xd->dev, "missing XDomain properties in response\n"); + goto err_free_dir; + } + + /* Release the existing one */ + if (xd->properties) { + tb_property_free_dir(xd->properties); + update = true; + } + + xd->properties = dir; + xd->property_block_gen = gen; + + tb_xdomain_restore_paths(xd); + + mutex_unlock(&xd->lock); + + kfree(block); + + /* + * Now the device should be ready enough so we can add it to the + * bus and let userspace know about it. If the device is already + * registered, we notify the userspace that it has changed. + */ + if (!update) { + if (device_add(&xd->dev)) { + dev_err(&xd->dev, "failed to add XDomain device\n"); + return; + } + } else { + kobject_uevent(&xd->dev.kobj, KOBJ_CHANGE); + } + + enumerate_services(xd); + return; + +err_free_dir: + tb_property_free_dir(dir); +err_free_block: + kfree(block); + mutex_unlock(&xd->lock); +} + +static void tb_xdomain_properties_changed(struct work_struct *work) +{ + struct tb_xdomain *xd = container_of(work, typeof(*xd), + properties_changed_work.work); + int ret; + + ret = tb_xdp_properties_changed_request(xd->tb->ctl, xd->route, + xd->properties_changed_retries, xd->local_uuid); + if (ret) { + if (xd->properties_changed_retries-- > 0) + queue_delayed_work(xd->tb->wq, + &xd->properties_changed_work, + msecs_to_jiffies(1000)); + return; + } + + xd->properties_changed_retries = XDOMAIN_PROPERTIES_CHANGED_RETRIES; +} + +static ssize_t device_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + + return sprintf(buf, "%#x\n", xd->device); +} +static DEVICE_ATTR_RO(device); + +static ssize_t +device_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + int ret; + + if (mutex_lock_interruptible(&xd->lock)) + return -ERESTARTSYS; + ret = sprintf(buf, "%s\n", xd->device_name ? xd->device_name : ""); + mutex_unlock(&xd->lock); + + return ret; +} +static DEVICE_ATTR_RO(device_name); + +static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + + return sprintf(buf, "%#x\n", xd->vendor); +} +static DEVICE_ATTR_RO(vendor); + +static ssize_t +vendor_name_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + int ret; + + if (mutex_lock_interruptible(&xd->lock)) + return -ERESTARTSYS; + ret = sprintf(buf, "%s\n", xd->vendor_name ? xd->vendor_name : ""); + mutex_unlock(&xd->lock); + + return ret; +} +static DEVICE_ATTR_RO(vendor_name); + +static ssize_t unique_id_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + + return sprintf(buf, "%pUb\n", xd->remote_uuid); +} +static DEVICE_ATTR_RO(unique_id); + +static struct attribute *xdomain_attrs[] = { + &dev_attr_device.attr, + &dev_attr_device_name.attr, + &dev_attr_unique_id.attr, + &dev_attr_vendor.attr, + &dev_attr_vendor_name.attr, + NULL, +}; + +static struct attribute_group xdomain_attr_group = { + .attrs = xdomain_attrs, +}; + +static const struct attribute_group *xdomain_attr_groups[] = { + &xdomain_attr_group, + NULL, +}; + +static void tb_xdomain_release(struct device *dev) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + + put_device(xd->dev.parent); + + tb_property_free_dir(xd->properties); + ida_destroy(&xd->service_ids); + + kfree(xd->local_uuid); + kfree(xd->remote_uuid); + kfree(xd->device_name); + kfree(xd->vendor_name); + kfree(xd); +} + +static void start_handshake(struct tb_xdomain *xd) +{ + xd->properties_retries = XDOMAIN_PROPERTIES_RETRIES; + xd->properties_changed_retries = XDOMAIN_PROPERTIES_CHANGED_RETRIES; + + /* Start exchanging properties with the other host */ + queue_delayed_work(xd->tb->wq, &xd->properties_changed_work, + msecs_to_jiffies(100)); + queue_delayed_work(xd->tb->wq, &xd->get_properties_work, + msecs_to_jiffies(1000)); +} + +static void stop_handshake(struct tb_xdomain *xd) +{ + xd->properties_retries = 0; + xd->properties_changed_retries = 0; + + cancel_delayed_work_sync(&xd->get_properties_work); + cancel_delayed_work_sync(&xd->properties_changed_work); +} + +static int __maybe_unused tb_xdomain_suspend(struct device *dev) +{ + stop_handshake(tb_to_xdomain(dev)); + return 0; +} + +static int __maybe_unused tb_xdomain_resume(struct device *dev) +{ + struct tb_xdomain *xd = tb_to_xdomain(dev); + + /* + * Ask tb_xdomain_get_properties() restore any existing DMA + * paths after properties are re-read. + */ + xd->resume = true; + start_handshake(xd); + + return 0; +} + +static const struct dev_pm_ops tb_xdomain_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tb_xdomain_suspend, tb_xdomain_resume) +}; + +struct device_type tb_xdomain_type = { + .name = "thunderbolt_xdomain", + .release = tb_xdomain_release, + .pm = &tb_xdomain_pm_ops, +}; +EXPORT_SYMBOL_GPL(tb_xdomain_type); + +/** + * tb_xdomain_alloc() - Allocate new XDomain object + * @tb: Domain where the XDomain belongs + * @parent: Parent device (the switch through the connection to the + * other domain is reached). + * @route: Route string used to reach the other domain + * @local_uuid: Our local domain UUID + * @remote_uuid: UUID of the other domain + * + * Allocates new XDomain structure and returns pointer to that. The + * object must be released by calling tb_xdomain_put(). + */ +struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent, + u64 route, const uuid_t *local_uuid, + const uuid_t *remote_uuid) +{ + struct tb_xdomain *xd; + + xd = kzalloc(sizeof(*xd), GFP_KERNEL); + if (!xd) + return NULL; + + xd->tb = tb; + xd->route = route; + ida_init(&xd->service_ids); + mutex_init(&xd->lock); + INIT_DELAYED_WORK(&xd->get_properties_work, tb_xdomain_get_properties); + INIT_DELAYED_WORK(&xd->properties_changed_work, + tb_xdomain_properties_changed); + + xd->local_uuid = kmemdup(local_uuid, sizeof(uuid_t), GFP_KERNEL); + if (!xd->local_uuid) + goto err_free; + + xd->remote_uuid = kmemdup(remote_uuid, sizeof(uuid_t), GFP_KERNEL); + if (!xd->remote_uuid) + goto err_free_local_uuid; + + device_initialize(&xd->dev); + xd->dev.parent = get_device(parent); + xd->dev.bus = &tb_bus_type; + xd->dev.type = &tb_xdomain_type; + xd->dev.groups = xdomain_attr_groups; + dev_set_name(&xd->dev, "%u-%llx", tb->index, route); + + return xd; + +err_free_local_uuid: + kfree(xd->local_uuid); +err_free: + kfree(xd); + + return NULL; +} + +/** + * tb_xdomain_add() - Add XDomain to the bus + * @xd: XDomain to add + * + * This function starts XDomain discovery protocol handshake and + * eventually adds the XDomain to the bus. After calling this function + * the caller needs to call tb_xdomain_remove() in order to remove and + * release the object regardless whether the handshake succeeded or not. + */ +void tb_xdomain_add(struct tb_xdomain *xd) +{ + /* Start exchanging properties with the other host */ + start_handshake(xd); +} + +static int unregister_service(struct device *dev, void *data) +{ + device_unregister(dev); + return 0; +} + +/** + * tb_xdomain_remove() - Remove XDomain from the bus + * @xd: XDomain to remove + * + * This will stop all ongoing configuration work and remove the XDomain + * along with any services from the bus. When the last reference to @xd + * is released the object will be released as well. + */ +void tb_xdomain_remove(struct tb_xdomain *xd) +{ + stop_handshake(xd); + + device_for_each_child_reverse(&xd->dev, xd, unregister_service); + + if (!device_is_registered(&xd->dev)) + put_device(&xd->dev); + else + device_unregister(&xd->dev); +} + +/** + * tb_xdomain_enable_paths() - Enable DMA paths for XDomain connection + * @xd: XDomain connection + * @transmit_path: HopID of the transmit path the other end is using to + * send packets + * @transmit_ring: DMA ring used to receive packets from the other end + * @receive_path: HopID of the receive path the other end is using to + * receive packets + * @receive_ring: DMA ring used to send packets to the other end + * + * The function enables DMA paths accordingly so that after successful + * return the caller can send and receive packets using high-speed DMA + * path. + * + * Return: %0 in case of success and negative errno in case of error + */ +int tb_xdomain_enable_paths(struct tb_xdomain *xd, u16 transmit_path, + u16 transmit_ring, u16 receive_path, + u16 receive_ring) +{ + int ret; + + mutex_lock(&xd->lock); + + if (xd->transmit_path) { + ret = xd->transmit_path == transmit_path ? 0 : -EBUSY; + goto exit_unlock; + } + + xd->transmit_path = transmit_path; + xd->transmit_ring = transmit_ring; + xd->receive_path = receive_path; + xd->receive_ring = receive_ring; + + ret = tb_domain_approve_xdomain_paths(xd->tb, xd); + +exit_unlock: + mutex_unlock(&xd->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(tb_xdomain_enable_paths); + +/** + * tb_xdomain_disable_paths() - Disable DMA paths for XDomain connection + * @xd: XDomain connection + * + * This does the opposite of tb_xdomain_enable_paths(). After call to + * this the caller is not expected to use the rings anymore. + * + * Return: %0 in case of success and negative errno in case of error + */ +int tb_xdomain_disable_paths(struct tb_xdomain *xd) +{ + int ret = 0; + + mutex_lock(&xd->lock); + if (xd->transmit_path) { + xd->transmit_path = 0; + xd->transmit_ring = 0; + xd->receive_path = 0; + xd->receive_ring = 0; + + ret = tb_domain_disconnect_xdomain_paths(xd->tb, xd); + } + mutex_unlock(&xd->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(tb_xdomain_disable_paths); + +struct tb_xdomain_lookup { + const uuid_t *uuid; + u8 link; + u8 depth; +}; + +static struct tb_xdomain *switch_find_xdomain(struct tb_switch *sw, + const struct tb_xdomain_lookup *lookup) +{ + int i; + + for (i = 1; i <= sw->config.max_port_number; i++) { + struct tb_port *port = &sw->ports[i]; + struct tb_xdomain *xd; + + if (tb_is_upstream_port(port)) + continue; + + if (port->xdomain) { + xd = port->xdomain; + + if (lookup->uuid) { + if (uuid_equal(xd->remote_uuid, lookup->uuid)) + return xd; + } else if (lookup->link == xd->link && + lookup->depth == xd->depth) { + return xd; + } + } else if (port->remote) { + xd = switch_find_xdomain(port->remote->sw, lookup); + if (xd) + return xd; + } + } + + return NULL; +} + +/** + * tb_xdomain_find_by_uuid() - Find an XDomain by UUID + * @tb: Domain where the XDomain belongs to + * @uuid: UUID to look for + * + * Finds XDomain by walking through the Thunderbolt topology below @tb. + * The returned XDomain will have its reference count increased so the + * caller needs to call tb_xdomain_put() when it is done with the + * object. + * + * This will find all XDomains including the ones that are not yet added + * to the bus (handshake is still in progress). + * + * The caller needs to hold @tb->lock. + */ +struct tb_xdomain *tb_xdomain_find_by_uuid(struct tb *tb, const uuid_t *uuid) +{ + struct tb_xdomain_lookup lookup; + struct tb_xdomain *xd; + + memset(&lookup, 0, sizeof(lookup)); + lookup.uuid = uuid; + + xd = switch_find_xdomain(tb->root_switch, &lookup); + if (xd) { + get_device(&xd->dev); + return xd; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(tb_xdomain_find_by_uuid); + +/** + * tb_xdomain_find_by_link_depth() - Find an XDomain by link and depth + * @tb: Domain where the XDomain belongs to + * @link: Root switch link number + * @depth: Depth in the link + * + * Finds XDomain by walking through the Thunderbolt topology below @tb. + * The returned XDomain will have its reference count increased so the + * caller needs to call tb_xdomain_put() when it is done with the + * object. + * + * This will find all XDomains including the ones that are not yet added + * to the bus (handshake is still in progress). + * + * The caller needs to hold @tb->lock. + */ +struct tb_xdomain *tb_xdomain_find_by_link_depth(struct tb *tb, u8 link, + u8 depth) +{ + struct tb_xdomain_lookup lookup; + struct tb_xdomain *xd; + + memset(&lookup, 0, sizeof(lookup)); + lookup.link = link; + lookup.depth = depth; + + xd = switch_find_xdomain(tb->root_switch, &lookup); + if (xd) { + get_device(&xd->dev); + return xd; + } + + return NULL; +} + +bool tb_xdomain_handle_request(struct tb *tb, enum tb_cfg_pkg_type type, + const void *buf, size_t size) +{ + const struct tb_protocol_handler *handler, *tmp; + const struct tb_xdp_header *hdr = buf; + unsigned int length; + int ret = 0; + + /* We expect the packet is at least size of the header */ + length = hdr->xd_hdr.length_sn & TB_XDOMAIN_LENGTH_MASK; + if (length != size / 4 - sizeof(hdr->xd_hdr) / 4) + return true; + if (length < sizeof(*hdr) / 4 - sizeof(hdr->xd_hdr) / 4) + return true; + + /* + * Handle XDomain discovery protocol packets directly here. For + * other protocols (based on their UUID) we call registered + * handlers in turn. + */ + if (uuid_equal(&hdr->uuid, &tb_xdp_uuid)) { + if (type == TB_CFG_PKG_XDOMAIN_REQ) { + tb_xdp_schedule_request(tb, hdr, size); + return true; + } + return false; + } + + mutex_lock(&xdomain_lock); + list_for_each_entry_safe(handler, tmp, &protocol_handlers, list) { + if (!uuid_equal(&hdr->uuid, handler->uuid)) + continue; + + mutex_unlock(&xdomain_lock); + ret = handler->callback(buf, size, handler->data); + mutex_lock(&xdomain_lock); + + if (ret) + break; + } + mutex_unlock(&xdomain_lock); + + return ret > 0; +} + +static int rebuild_property_block(void) +{ + u32 *block, len; + int ret; + + ret = tb_property_format_dir(xdomain_property_dir, NULL, 0); + if (ret < 0) + return ret; + + len = ret; + + block = kcalloc(len, sizeof(u32), GFP_KERNEL); + if (!block) + return -ENOMEM; + + ret = tb_property_format_dir(xdomain_property_dir, block, len); + if (ret) { + kfree(block); + return ret; + } + + kfree(xdomain_property_block); + xdomain_property_block = block; + xdomain_property_block_len = len; + xdomain_property_block_gen++; + + return 0; +} + +static int update_xdomain(struct device *dev, void *data) +{ + struct tb_xdomain *xd; + + xd = tb_to_xdomain(dev); + if (xd) { + queue_delayed_work(xd->tb->wq, &xd->properties_changed_work, + msecs_to_jiffies(50)); + } + + return 0; +} + +static void update_all_xdomains(void) +{ + bus_for_each_dev(&tb_bus_type, NULL, NULL, update_xdomain); +} + +static bool remove_directory(const char *key, const struct tb_property_dir *dir) +{ + struct tb_property *p; + + p = tb_property_find(xdomain_property_dir, key, + TB_PROPERTY_TYPE_DIRECTORY); + if (p && p->value.dir == dir) { + tb_property_remove(p); + return true; + } + return false; +} + +/** + * tb_register_property_dir() - Register property directory to the host + * @key: Key (name) of the directory to add + * @dir: Directory to add + * + * Service drivers can use this function to add new property directory + * to the host available properties. The other connected hosts are + * notified so they can re-read properties of this host if they are + * interested. + * + * Return: %0 on success and negative errno on failure + */ +int tb_register_property_dir(const char *key, struct tb_property_dir *dir) +{ + int ret; + + if (!key || strlen(key) > 8) + return -EINVAL; + + mutex_lock(&xdomain_lock); + if (tb_property_find(xdomain_property_dir, key, + TB_PROPERTY_TYPE_DIRECTORY)) { + ret = -EEXIST; + goto err_unlock; + } + + ret = tb_property_add_dir(xdomain_property_dir, key, dir); + if (ret) + goto err_unlock; + + ret = rebuild_property_block(); + if (ret) { + remove_directory(key, dir); + goto err_unlock; + } + + mutex_unlock(&xdomain_lock); + update_all_xdomains(); + return 0; + +err_unlock: + mutex_unlock(&xdomain_lock); + return ret; +} +EXPORT_SYMBOL_GPL(tb_register_property_dir); + +/** + * tb_unregister_property_dir() - Removes property directory from host + * @key: Key (name) of the directory + * @dir: Directory to remove + * + * This will remove the existing directory from this host and notify the + * connected hosts about the change. + */ +void tb_unregister_property_dir(const char *key, struct tb_property_dir *dir) +{ + int ret = 0; + + mutex_lock(&xdomain_lock); + if (remove_directory(key, dir)) + ret = rebuild_property_block(); + mutex_unlock(&xdomain_lock); + + if (!ret) + update_all_xdomains(); +} +EXPORT_SYMBOL_GPL(tb_unregister_property_dir); + +int tb_xdomain_init(void) +{ + int ret; + + xdomain_property_dir = tb_property_create_dir(NULL); + if (!xdomain_property_dir) + return -ENOMEM; + + /* + * Initialize standard set of properties without any service + * directories. Those will be added by service drivers + * themselves when they are loaded. + */ + tb_property_add_immediate(xdomain_property_dir, "vendorid", + PCI_VENDOR_ID_INTEL); + tb_property_add_text(xdomain_property_dir, "vendorid", "Intel Corp."); + tb_property_add_immediate(xdomain_property_dir, "deviceid", 0x1); + tb_property_add_text(xdomain_property_dir, "deviceid", + utsname()->nodename); + tb_property_add_immediate(xdomain_property_dir, "devicerv", 0x80000100); + + ret = rebuild_property_block(); + if (ret) { + tb_property_free_dir(xdomain_property_dir); + xdomain_property_dir = NULL; + } + + return ret; +} + +void tb_xdomain_exit(void) +{ + kfree(xdomain_property_block); + tb_property_free_dir(xdomain_property_dir); +} diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 694cebb50f72..7625c3b81f84 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -683,5 +683,31 @@ struct fsl_mc_device_id { const char obj_type[16]; }; +/** + * struct tb_service_id - Thunderbolt service identifiers + * @match_flags: Flags used to match the structure + * @protocol_key: Protocol key the service supports + * @protocol_id: Protocol id the service supports + * @protocol_version: Version of the protocol + * @protocol_revision: Revision of the protocol software + * @driver_data: Driver specific data + * + * Thunderbolt XDomain services are exposed as devices where each device + * carries the protocol information the service supports. Thunderbolt + * XDomain service drivers match against that information. + */ +struct tb_service_id { + __u32 match_flags; + char protocol_key[8 + 1]; + __u32 protocol_id; + __u32 protocol_version; + __u32 protocol_revision; + kernel_ulong_t driver_data; +}; + +#define TBSVC_MATCH_PROTOCOL_KEY 0x0001 +#define TBSVC_MATCH_PROTOCOL_ID 0x0002 +#define TBSVC_MATCH_PROTOCOL_VERSION 0x0004 +#define TBSVC_MATCH_PROTOCOL_REVISION 0x0008 #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 43b8d1e09341..18c0e3d5e85c 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -17,6 +17,7 @@ #include #include #include +#include #include enum tb_cfg_pkg_type { @@ -77,6 +78,8 @@ struct tb { }; extern struct bus_type tb_bus_type; +extern struct device_type tb_service_type; +extern struct device_type tb_xdomain_type; #define TB_LINKS_PER_PHY_PORT 2 @@ -155,4 +158,243 @@ struct tb_property *tb_property_get_next(struct tb_property_dir *dir, property; \ property = tb_property_get_next(dir, property)) +int tb_register_property_dir(const char *key, struct tb_property_dir *dir); +void tb_unregister_property_dir(const char *key, struct tb_property_dir *dir); + +/** + * struct tb_xdomain - Cross-domain (XDomain) connection + * @dev: XDomain device + * @tb: Pointer to the domain + * @remote_uuid: UUID of the remote domain (host) + * @local_uuid: Cached local UUID + * @route: Route string the other domain can be reached + * @vendor: Vendor ID of the remote domain + * @device: Device ID of the demote domain + * @lock: Lock to serialize access to the following fields of this structure + * @vendor_name: Name of the vendor (or %NULL if not known) + * @device_name: Name of the device (or %NULL if not known) + * @is_unplugged: The XDomain is unplugged + * @resume: The XDomain is being resumed + * @transmit_path: HopID which the remote end expects us to transmit + * @transmit_ring: Local ring (hop) where outgoing packets are pushed + * @receive_path: HopID which we expect the remote end to transmit + * @receive_ring: Local ring (hop) where incoming packets arrive + * @service_ids: Used to generate IDs for the services + * @properties: Properties exported by the remote domain + * @property_block_gen: Generation of @properties + * @properties_lock: Lock protecting @properties. + * @get_properties_work: Work used to get remote domain properties + * @properties_retries: Number of times left to read properties + * @properties_changed_work: Work used to notify the remote domain that + * our properties have changed + * @properties_changed_retries: Number of times left to send properties + * changed notification + * @link: Root switch link the remote domain is connected (ICM only) + * @depth: Depth in the chain the remote domain is connected (ICM only) + * + * This structure represents connection across two domains (hosts). + * Each XDomain contains zero or more services which are exposed as + * &struct tb_service objects. + * + * Service drivers may access this structure if they need to enumerate + * non-standard properties but they need hold @lock when doing so + * because properties can be changed asynchronously in response to + * changes in the remote domain. + */ +struct tb_xdomain { + struct device dev; + struct tb *tb; + uuid_t *remote_uuid; + const uuid_t *local_uuid; + u64 route; + u16 vendor; + u16 device; + struct mutex lock; + const char *vendor_name; + const char *device_name; + bool is_unplugged; + bool resume; + u16 transmit_path; + u16 transmit_ring; + u16 receive_path; + u16 receive_ring; + struct ida service_ids; + struct tb_property_dir *properties; + u32 property_block_gen; + struct delayed_work get_properties_work; + int properties_retries; + struct delayed_work properties_changed_work; + int properties_changed_retries; + u8 link; + u8 depth; +}; + +int tb_xdomain_enable_paths(struct tb_xdomain *xd, u16 transmit_path, + u16 transmit_ring, u16 receive_path, + u16 receive_ring); +int tb_xdomain_disable_paths(struct tb_xdomain *xd); +struct tb_xdomain *tb_xdomain_find_by_uuid(struct tb *tb, const uuid_t *uuid); + +static inline struct tb_xdomain * +tb_xdomain_find_by_uuid_locked(struct tb *tb, const uuid_t *uuid) +{ + struct tb_xdomain *xd; + + mutex_lock(&tb->lock); + xd = tb_xdomain_find_by_uuid(tb, uuid); + mutex_unlock(&tb->lock); + + return xd; +} + +static inline struct tb_xdomain *tb_xdomain_get(struct tb_xdomain *xd) +{ + if (xd) + get_device(&xd->dev); + return xd; +} + +static inline void tb_xdomain_put(struct tb_xdomain *xd) +{ + if (xd) + put_device(&xd->dev); +} + +static inline bool tb_is_xdomain(const struct device *dev) +{ + return dev->type == &tb_xdomain_type; +} + +static inline struct tb_xdomain *tb_to_xdomain(struct device *dev) +{ + if (tb_is_xdomain(dev)) + return container_of(dev, struct tb_xdomain, dev); + return NULL; +} + +int tb_xdomain_response(struct tb_xdomain *xd, const void *response, + size_t size, enum tb_cfg_pkg_type type); +int tb_xdomain_request(struct tb_xdomain *xd, const void *request, + size_t request_size, enum tb_cfg_pkg_type request_type, + void *response, size_t response_size, + enum tb_cfg_pkg_type response_type, + unsigned int timeout_msec); + +/** + * tb_protocol_handler - Protocol specific handler + * @uuid: XDomain messages with this UUID are dispatched to this handler + * @callback: Callback called with the XDomain message. Returning %1 + * here tells the XDomain core that the message was handled + * by this handler and should not be forwared to other + * handlers. + * @data: Data passed with the callback + * @list: Handlers are linked using this + * + * Thunderbolt services can hook into incoming XDomain requests by + * registering protocol handler. Only limitation is that the XDomain + * discovery protocol UUID cannot be registered since it is handled by + * the core XDomain code. + * + * The @callback must check that the message is really directed to the + * service the driver implements. + */ +struct tb_protocol_handler { + const uuid_t *uuid; + int (*callback)(const void *buf, size_t size, void *data); + void *data; + struct list_head list; +}; + +int tb_register_protocol_handler(struct tb_protocol_handler *handler); +void tb_unregister_protocol_handler(struct tb_protocol_handler *handler); + +/** + * struct tb_service - Thunderbolt service + * @dev: XDomain device + * @id: ID of the service (shown in sysfs) + * @key: Protocol key from the properties directory + * @prtcid: Protocol ID from the properties directory + * @prtcvers: Protocol version from the properties directory + * @prtcrevs: Protocol software revision from the properties directory + * @prtcstns: Protocol settings mask from the properties directory + * + * Each domain exposes set of services it supports as collection of + * properties. For each service there will be one corresponding + * &struct tb_service. Service drivers are bound to these. + */ +struct tb_service { + struct device dev; + int id; + const char *key; + u32 prtcid; + u32 prtcvers; + u32 prtcrevs; + u32 prtcstns; +}; + +static inline struct tb_service *tb_service_get(struct tb_service *svc) +{ + if (svc) + get_device(&svc->dev); + return svc; +} + +static inline void tb_service_put(struct tb_service *svc) +{ + if (svc) + put_device(&svc->dev); +} + +static inline bool tb_is_service(const struct device *dev) +{ + return dev->type == &tb_service_type; +} + +static inline struct tb_service *tb_to_service(struct device *dev) +{ + if (tb_is_service(dev)) + return container_of(dev, struct tb_service, dev); + return NULL; +} + +/** + * tb_service_driver - Thunderbolt service driver + * @driver: Driver structure + * @probe: Called when the driver is probed + * @remove: Called when the driver is removed (optional) + * @shutdown: Called at shutdown time to stop the service (optional) + * @id_table: Table of service identifiers the driver supports + */ +struct tb_service_driver { + struct device_driver driver; + int (*probe)(struct tb_service *svc, const struct tb_service_id *id); + void (*remove)(struct tb_service *svc); + void (*shutdown)(struct tb_service *svc); + const struct tb_service_id *id_table; +}; + +#define TB_SERVICE(key, id) \ + .match_flags = TBSVC_MATCH_PROTOCOL_KEY | \ + TBSVC_MATCH_PROTOCOL_ID, \ + .protocol_key = (key), \ + .protocol_id = (id) + +int tb_register_service_driver(struct tb_service_driver *drv); +void tb_unregister_service_driver(struct tb_service_driver *drv); + +static inline void *tb_service_get_drvdata(const struct tb_service *svc) +{ + return dev_get_drvdata(&svc->dev); +} + +static inline void tb_service_set_drvdata(struct tb_service *svc, void *data) +{ + dev_set_drvdata(&svc->dev, data); +} + +static inline struct tb_xdomain *tb_service_parent(struct tb_service *svc) +{ + return tb_to_xdomain(svc->dev.parent); +} + #endif /* THUNDERBOLT_H_ */ diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index e4d90e50f6fe..57263f2f8f2f 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c @@ -206,5 +206,12 @@ int main(void) DEVID_FIELD(fsl_mc_device_id, vendor); DEVID_FIELD(fsl_mc_device_id, obj_type); + DEVID(tb_service_id); + DEVID_FIELD(tb_service_id, match_flags); + DEVID_FIELD(tb_service_id, protocol_key); + DEVID_FIELD(tb_service_id, protocol_id); + DEVID_FIELD(tb_service_id, protocol_version); + DEVID_FIELD(tb_service_id, protocol_revision); + return 0; } diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 29d6699d5a06..6ef6e63f96fd 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1301,6 +1301,31 @@ static int do_fsl_mc_entry(const char *filename, void *symval, } ADD_TO_DEVTABLE("fslmc", fsl_mc_device_id, do_fsl_mc_entry); +/* Looks like: tbsvc:kSpNvNrN */ +static int do_tbsvc_entry(const char *filename, void *symval, char *alias) +{ + DEF_FIELD(symval, tb_service_id, match_flags); + DEF_FIELD_ADDR(symval, tb_service_id, protocol_key); + DEF_FIELD(symval, tb_service_id, protocol_id); + DEF_FIELD(symval, tb_service_id, protocol_version); + DEF_FIELD(symval, tb_service_id, protocol_revision); + + strcpy(alias, "tbsvc:"); + if (match_flags & TBSVC_MATCH_PROTOCOL_KEY) + sprintf(alias + strlen(alias), "k%s", *protocol_key); + else + strcat(alias + strlen(alias), "k*"); + ADD(alias, "p", match_flags & TBSVC_MATCH_PROTOCOL_ID, protocol_id); + ADD(alias, "v", match_flags & TBSVC_MATCH_PROTOCOL_VERSION, + protocol_version); + ADD(alias, "r", match_flags & TBSVC_MATCH_PROTOCOL_REVISION, + protocol_revision); + + add_wildcard(alias); + return 1; +} +ADD_TO_DEVTABLE("tbsvc", tb_service_id, do_tbsvc_entry); + /* Does namelen bytes of name exactly match the symbol? */ static bool sym_is(const char *name, unsigned namelen, const char *symbol) { -- cgit v1.3-6-gb490 From 4322323058f010274564006d61945187a15b6361 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Sun, 24 Sep 2017 18:27:25 -0500 Subject: scripts/dtc: add fdt_overlay.c and fdt_addresses.c to sync script libfdt has gained some new files. We need to include them in the kernel's copy. Reported-by: Kyle Yan Signed-off-by: Rob Herring --- scripts/dtc/update-dtc-source.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/dtc/update-dtc-source.sh b/scripts/dtc/update-dtc-source.sh index b8ebcc6722d2..f3e5c596050a 100755 --- a/scripts/dtc/update-dtc-source.sh +++ b/scripts/dtc/update-dtc-source.sh @@ -34,7 +34,9 @@ DTC_SOURCE="checks.c data.c dtc.c dtc.h flattree.c fstree.c livetree.c srcpos.c srcpos.h treesource.c util.c util.h version_gen.h Makefile.dtc \ dtc-lexer.l dtc-parser.y" DTC_GENERATED="dtc-lexer.lex.c dtc-parser.tab.c dtc-parser.tab.h" -LIBFDT_SOURCE="Makefile.libfdt fdt.c fdt.h fdt_empty_tree.c fdt_ro.c fdt_rw.c fdt_strerror.c fdt_sw.c fdt_wip.c libfdt.h libfdt_env.h libfdt_internal.h" +LIBFDT_SOURCE="Makefile.libfdt fdt.c fdt.h fdt_addresses.c fdt_empty_tree.c \ + fdt_overlay.c fdt_ro.c fdt_rw.c fdt_strerror.c fdt_sw.c \ + fdt_wip.c libfdt.h libfdt_env.h libfdt_internal.h" get_last_dtc_version() { git log --oneline scripts/dtc/ | grep 'upstream' | head -1 | sed -e 's/^.* \(.*\)/\1/' -- cgit v1.3-6-gb490 From 4201d057ea91c3d6efd2db65219bc91fae413bc2 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 3 Oct 2017 11:37:04 -0500 Subject: scripts/dtc: Update to upstream version v1.4.5-3-gb1a60033c110 This adds the following commits from upstream: b1a60033c110 tests: Add a test for overlays syntactic sugar 737b2df39cc8 overlay: Add syntactic sugar version of overlays 497432fd2131 checks: Use proper format modifier for size_t 22a65c5331c2 dtc: Bump version to v1.4.5 c575d8059fff Add fdtoverlay to .gitignore b6a6f9490d19 fdtoverlay: Sanity check blob size 8c1eb1526d2d pylibfdt: Use Python2 explicitly ee3d26f6960b checks: add interrupts property check c1e7738988f5 checks: add gpio binding properties check b3bbac02d5e3 checks: add phandle with arg property checks fe50bd1ecc1d fdtget: Split out cell list display into a new function 62d812308d11 README: Add a note about test_tree1.dts 5bed86aee9e8 pylibfdt: Add support for fdt_subnode_offset() 46f31b65b3b3 pylibfdt: Add support for fdt_node_offset_by_phandle() a3ae43723687 pylibfdt: Add support for fdt_parent_offset() a198af80344c pylibfdt: Add support for fdt_get_phandle() b9eba92ea50f tests: Return a failure code when any tests fail 155faf6cc209 pylibfdt: Use local pylibfdt module 50e5cd07f325 pylibfdt: Add a test for use of uint32_t ab78860f09f5 pylibfdt: Add stdint include to fix uint32_t 36f511fb1113 tests: Add stacked overlay tests on fdtoverlay 1bb00655d3e5 fdt: Allow stacked overlays phandle references a33c2247ac8d Introduce fdt_setprop_placeholder() method 0016f8c2aa32 dtc: change default phandles to ePAPR style instead of both e3b9a9588a35 tests: fdtoverlay unit test 42409146f2db fdtoverlay: A tool that applies overlays aae22722fc8d manual: Document missing options 13ce6e1c2fc4 dtc: fix sprintf() format string error, again d990b8013889 Makefile: Fix build on MSYS2 and Cygwin 51f56dedf8ea Clean up shared library compile/link options 21a2bc896e3d Suppress expected error message in fdtdump test 2a42b14d0d03 dtc: check.c fix compile error a10cb3c818d3 Fix get_node_by_path string equality check 548aea2c436a fdtdump: Discourage use of fdtdump c2258841a785 fdtdump: Fix over-zealous version check 9067ee4be0e6 Fix a few whitespace and style nits e56f2b07be38 pylibfdt: Use setup.py to build the swig file 896f1c133265 pylibfdt: Use Makefile constructs to implement NO_PYTHON 90db6d9989ca pylibfdt: Allow setup.py to operate stand-alone e20d9658cd8f Add Coverity Scan support b04a2cf08862 pylibfdt: Fix code style in setup.py 1c5170d3a466 pylibfdt: Rename libfdt.swig to libfdt.i 580a9f6c2880 Add a libfdt function to write a property placeholder ab15256d8d02 pylibfdt: Use the call function to simplify the Makefile 9f2e3a3a1f19 pylibfdt: Use the correct libfdt version in the module e91c652af215 pylibfdt: Enable installation of Python module 8a892fd85d94 pylibfdt: Allow building to be disabled 741cdff85d3e .travis.yml: Add builds with and without Python library prerequisites 14c4171f4f9a pylibfdt: Use package_dir to set the package directory 89a5062ab231 pylibfdt: Use environment to pass C flags and files 4e0e0d049757 pylibfdt: Allow pkg-config to be supplied in the environment 6afd7d9688f5 Correct typo: s/pylibgfdt/pylibfdt/ Signed-off-by: Rob Herring --- scripts/dtc/checks.c | 282 +++++++++++- scripts/dtc/dtc-lexer.lex.c_shipped | 10 +- scripts/dtc/dtc-parser.tab.c_shipped | 430 ++++++++--------- scripts/dtc/dtc-parser.y | 20 +- scripts/dtc/dtc.c | 2 +- scripts/dtc/dtc.h | 3 + scripts/dtc/libfdt/fdt_addresses.c | 96 ++++ scripts/dtc/libfdt/fdt_empty_tree.c | 1 - scripts/dtc/libfdt/fdt_overlay.c | 861 +++++++++++++++++++++++++++++++++++ scripts/dtc/libfdt/fdt_ro.c | 4 +- scripts/dtc/libfdt/fdt_rw.c | 24 +- scripts/dtc/libfdt/fdt_sw.c | 16 +- scripts/dtc/libfdt/fdt_wip.c | 4 +- scripts/dtc/libfdt/libfdt.h | 47 ++ scripts/dtc/livetree.c | 31 +- scripts/dtc/version_gen.h | 2 +- 16 files changed, 1603 insertions(+), 230 deletions(-) create mode 100644 scripts/dtc/libfdt/fdt_addresses.c create mode 100644 scripts/dtc/libfdt/fdt_overlay.c (limited to 'scripts') diff --git a/scripts/dtc/checks.c b/scripts/dtc/checks.c index 62ea8f83d4a0..08a3a29edae3 100644 --- a/scripts/dtc/checks.c +++ b/scripts/dtc/checks.c @@ -873,7 +873,7 @@ static void check_simple_bus_reg(struct check *c, struct dt_info *dti, struct no while (size--) reg = (reg << 32) | fdt32_to_cpu(*(cells++)); - snprintf(unit_addr, sizeof(unit_addr), "%llx", (unsigned long long)reg); + snprintf(unit_addr, sizeof(unit_addr), "%"PRIx64, reg); if (!streq(unitname, unit_addr)) FAIL(c, dti, "Node %s simple-bus unit address format error, expected \"%s\"", node->fullpath, unit_addr); @@ -956,6 +956,265 @@ static void check_obsolete_chosen_interrupt_controller(struct check *c, WARNING(obsolete_chosen_interrupt_controller, check_obsolete_chosen_interrupt_controller, NULL); +struct provider { + const char *prop_name; + const char *cell_name; + bool optional; +}; + +static void check_property_phandle_args(struct check *c, + struct dt_info *dti, + struct node *node, + struct property *prop, + const struct provider *provider) +{ + struct node *root = dti->dt; + int cell, cellsize = 0; + + if (prop->val.len % sizeof(cell_t)) { + FAIL(c, dti, "property '%s' size (%d) is invalid, expected multiple of %zu in node %s", + prop->name, prop->val.len, sizeof(cell_t), node->fullpath); + return; + } + + for (cell = 0; cell < prop->val.len / sizeof(cell_t); cell += cellsize + 1) { + struct node *provider_node; + struct property *cellprop; + int phandle; + + phandle = propval_cell_n(prop, cell); + /* + * Some bindings use a cell value 0 or -1 to skip over optional + * entries when each index position has a specific definition. + */ + if (phandle == 0 || phandle == -1) { + cellsize = 0; + continue; + } + + /* If we have markers, verify the current cell is a phandle */ + if (prop->val.markers) { + struct marker *m = prop->val.markers; + for_each_marker_of_type(m, REF_PHANDLE) { + if (m->offset == (cell * sizeof(cell_t))) + break; + } + if (!m) + FAIL(c, dti, "Property '%s', cell %d is not a phandle reference in %s", + prop->name, cell, node->fullpath); + } + + provider_node = get_node_by_phandle(root, phandle); + if (!provider_node) { + FAIL(c, dti, "Could not get phandle node for %s:%s(cell %d)", + node->fullpath, prop->name, cell); + break; + } + + cellprop = get_property(provider_node, provider->cell_name); + if (cellprop) { + cellsize = propval_cell(cellprop); + } else if (provider->optional) { + cellsize = 0; + } else { + FAIL(c, dti, "Missing property '%s' in node %s or bad phandle (referred from %s:%s[%d])", + provider->cell_name, + provider_node->fullpath, + node->fullpath, prop->name, cell); + break; + } + + if (prop->val.len < ((cell + cellsize + 1) * sizeof(cell_t))) { + FAIL(c, dti, "%s property size (%d) too small for cell size %d in %s", + prop->name, prop->val.len, cellsize, node->fullpath); + } + } +} + +static void check_provider_cells_property(struct check *c, + struct dt_info *dti, + struct node *node) +{ + struct provider *provider = c->data; + struct property *prop; + + prop = get_property(node, provider->prop_name); + if (!prop) + return; + + check_property_phandle_args(c, dti, node, prop, provider); +} +#define WARNING_PROPERTY_PHANDLE_CELLS(nm, propname, cells_name, ...) \ + static struct provider nm##_provider = { (propname), (cells_name), __VA_ARGS__ }; \ + WARNING(nm##_property, check_provider_cells_property, &nm##_provider, &phandle_references); + +WARNING_PROPERTY_PHANDLE_CELLS(clocks, "clocks", "#clock-cells"); +WARNING_PROPERTY_PHANDLE_CELLS(cooling_device, "cooling-device", "#cooling-cells"); +WARNING_PROPERTY_PHANDLE_CELLS(dmas, "dmas", "#dma-cells"); +WARNING_PROPERTY_PHANDLE_CELLS(hwlocks, "hwlocks", "#hwlock-cells"); +WARNING_PROPERTY_PHANDLE_CELLS(interrupts_extended, "interrupts-extended", "#interrupt-cells"); +WARNING_PROPERTY_PHANDLE_CELLS(io_channels, "io-channels", "#io-channel-cells"); +WARNING_PROPERTY_PHANDLE_CELLS(iommus, "iommus", "#iommu-cells"); +WARNING_PROPERTY_PHANDLE_CELLS(mboxes, "mboxes", "#mbox-cells"); +WARNING_PROPERTY_PHANDLE_CELLS(msi_parent, "msi-parent", "#msi-cells", true); +WARNING_PROPERTY_PHANDLE_CELLS(mux_controls, "mux-controls", "#mux-control-cells"); +WARNING_PROPERTY_PHANDLE_CELLS(phys, "phys", "#phy-cells"); +WARNING_PROPERTY_PHANDLE_CELLS(power_domains, "power-domains", "#power-domain-cells"); +WARNING_PROPERTY_PHANDLE_CELLS(pwms, "pwms", "#pwm-cells"); +WARNING_PROPERTY_PHANDLE_CELLS(resets, "resets", "#reset-cells"); +WARNING_PROPERTY_PHANDLE_CELLS(sound_dais, "sound-dais", "#sound-dai-cells"); +WARNING_PROPERTY_PHANDLE_CELLS(thermal_sensors, "thermal-sensors", "#thermal-sensor-cells"); + +static bool prop_is_gpio(struct property *prop) +{ + char *str; + + /* + * *-gpios and *-gpio can appear in property names, + * so skip over any false matches (only one known ATM) + */ + if (strstr(prop->name, "nr-gpio")) + return false; + + str = strrchr(prop->name, '-'); + if (str) + str++; + else + str = prop->name; + if (!(streq(str, "gpios") || streq(str, "gpio"))) + return false; + + return true; +} + +static void check_gpios_property(struct check *c, + struct dt_info *dti, + struct node *node) +{ + struct property *prop; + + /* Skip GPIO hog nodes which have 'gpios' property */ + if (get_property(node, "gpio-hog")) + return; + + for_each_property(node, prop) { + struct provider provider; + + if (!prop_is_gpio(prop)) + continue; + + provider.prop_name = prop->name; + provider.cell_name = "#gpio-cells"; + provider.optional = false; + check_property_phandle_args(c, dti, node, prop, &provider); + } + +} +WARNING(gpios_property, check_gpios_property, NULL, &phandle_references); + +static void check_deprecated_gpio_property(struct check *c, + struct dt_info *dti, + struct node *node) +{ + struct property *prop; + + for_each_property(node, prop) { + char *str; + + if (!prop_is_gpio(prop)) + continue; + + str = strstr(prop->name, "gpio"); + if (!streq(str, "gpio")) + continue; + + FAIL(c, dti, "'[*-]gpio' is deprecated, use '[*-]gpios' instead for %s:%s", + node->fullpath, prop->name); + } + +} +CHECK(deprecated_gpio_property, check_deprecated_gpio_property, NULL); + +static bool node_is_interrupt_provider(struct node *node) +{ + struct property *prop; + + prop = get_property(node, "interrupt-controller"); + if (prop) + return true; + + prop = get_property(node, "interrupt-map"); + if (prop) + return true; + + return false; +} +static void check_interrupts_property(struct check *c, + struct dt_info *dti, + struct node *node) +{ + struct node *root = dti->dt; + struct node *irq_node = NULL, *parent = node; + struct property *irq_prop, *prop = NULL; + int irq_cells, phandle; + + irq_prop = get_property(node, "interrupts"); + if (!irq_prop) + return; + + if (irq_prop->val.len % sizeof(cell_t)) + FAIL(c, dti, "property '%s' size (%d) is invalid, expected multiple of %zu in node %s", + irq_prop->name, irq_prop->val.len, sizeof(cell_t), + node->fullpath); + + while (parent && !prop) { + if (parent != node && node_is_interrupt_provider(parent)) { + irq_node = parent; + break; + } + + prop = get_property(parent, "interrupt-parent"); + if (prop) { + phandle = propval_cell(prop); + irq_node = get_node_by_phandle(root, phandle); + if (!irq_node) { + FAIL(c, dti, "Bad interrupt-parent phandle for %s", + node->fullpath); + return; + } + if (!node_is_interrupt_provider(irq_node)) + FAIL(c, dti, + "Missing interrupt-controller or interrupt-map property in %s", + irq_node->fullpath); + + break; + } + + parent = parent->parent; + } + + if (!irq_node) { + FAIL(c, dti, "Missing interrupt-parent for %s", node->fullpath); + return; + } + + prop = get_property(irq_node, "#interrupt-cells"); + if (!prop) { + FAIL(c, dti, "Missing #interrupt-cells in interrupt-parent %s", + irq_node->fullpath); + return; + } + + irq_cells = propval_cell(prop); + if (irq_prop->val.len % (irq_cells * sizeof(cell_t))) { + FAIL(c, dti, + "interrupts size is (%d), expected multiple of %d in %s", + irq_prop->val.len, (int)(irq_cells * sizeof(cell_t)), + node->fullpath); + } +} +WARNING(interrupts_property, check_interrupts_property, &phandle_references); + static struct check *check_table[] = { &duplicate_node_names, &duplicate_property_names, &node_name_chars, &node_name_format, &property_name_chars, @@ -987,6 +1246,27 @@ static struct check *check_table[] = { &avoid_default_addr_size, &obsolete_chosen_interrupt_controller, + &clocks_property, + &cooling_device_property, + &dmas_property, + &hwlocks_property, + &interrupts_extended_property, + &io_channels_property, + &iommus_property, + &mboxes_property, + &msi_parent_property, + &mux_controls_property, + &phys_property, + &power_domains_property, + &pwms_property, + &resets_property, + &sound_dais_property, + &thermal_sensors_property, + + &deprecated_gpio_property, + &gpios_property, + &interrupts_property, + &always_fail, }; diff --git a/scripts/dtc/dtc-lexer.lex.c_shipped b/scripts/dtc/dtc-lexer.lex.c_shipped index 64c243772398..011bb9632ff2 100644 --- a/scripts/dtc/dtc-lexer.lex.c_shipped +++ b/scripts/dtc/dtc-lexer.lex.c_shipped @@ -1397,7 +1397,7 @@ static int yy_get_next_buffer (void) { char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; char *source = (yytext_ptr); - yy_size_t number_to_move, i; + int number_to_move, i; int ret_val; if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) @@ -1426,7 +1426,7 @@ static int yy_get_next_buffer (void) /* Try to read more data. */ /* First move last chars to start of buffer. */ - number_to_move = (yy_size_t) ((yy_c_buf_p) - (yytext_ptr)) - 1; + number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr) - 1); for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); @@ -1508,7 +1508,7 @@ static int yy_get_next_buffer (void) else ret_val = EOB_ACT_CONTINUE_SCAN; - if ((int) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + if (((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ); @@ -1987,10 +1987,10 @@ YY_BUFFER_STATE yy_scan_bytes (yyconst char * yybytes, int _yybytes_len ) YY_BUFFER_STATE b; char *buf; yy_size_t n; - yy_size_t i; + int i; /* Get memory for full buffer, including space for trailing EOB's. */ - n = (yy_size_t) _yybytes_len + 2; + n = (yy_size_t) (_yybytes_len + 2); buf = (char *) yyalloc(n ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" ); diff --git a/scripts/dtc/dtc-parser.tab.c_shipped b/scripts/dtc/dtc-parser.tab.c_shipped index 0a7a5ed86f04..aea514fa6928 100644 --- a/scripts/dtc/dtc-parser.tab.c_shipped +++ b/scripts/dtc/dtc-parser.tab.c_shipped @@ -448,7 +448,7 @@ union yyalloc /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 30 /* YYNRULES -- Number of rules. */ -#define YYNRULES 84 +#define YYNRULES 85 /* YYNSTATES -- Number of states. */ #define YYNSTATES 149 @@ -499,14 +499,14 @@ static const yytype_uint8 yytranslate[] = static const yytype_uint16 yyrline[] = { 0, 109, 109, 117, 121, 128, 129, 139, 142, 149, - 153, 161, 165, 170, 181, 191, 206, 214, 217, 224, - 228, 232, 236, 244, 248, 252, 256, 260, 276, 286, - 294, 297, 301, 308, 324, 329, 348, 362, 369, 370, - 371, 378, 382, 383, 387, 388, 392, 393, 397, 398, - 402, 403, 407, 408, 412, 413, 414, 418, 419, 420, - 421, 422, 426, 427, 428, 432, 433, 434, 438, 439, - 448, 457, 461, 462, 463, 464, 469, 472, 476, 484, - 487, 491, 499, 503, 507 + 153, 161, 165, 170, 181, 200, 213, 220, 228, 231, + 238, 242, 246, 250, 258, 262, 266, 270, 274, 290, + 300, 308, 311, 315, 322, 338, 343, 362, 376, 383, + 384, 385, 392, 396, 397, 401, 402, 406, 407, 411, + 412, 416, 417, 421, 422, 426, 427, 428, 432, 433, + 434, 435, 436, 440, 441, 442, 446, 447, 448, 452, + 453, 462, 471, 475, 476, 477, 478, 483, 486, 490, + 498, 501, 505, 513, 517, 521 }; #endif @@ -582,20 +582,20 @@ static const yytype_int8 yypact[] = static const yytype_uint8 yydefact[] = { 0, 0, 0, 5, 7, 3, 1, 6, 0, 0, - 0, 7, 0, 38, 39, 0, 0, 10, 0, 2, - 8, 4, 0, 0, 0, 72, 0, 41, 42, 44, - 46, 48, 50, 52, 54, 57, 64, 67, 71, 0, - 17, 11, 0, 0, 0, 0, 73, 74, 75, 40, + 16, 7, 0, 39, 40, 0, 0, 10, 0, 2, + 8, 4, 0, 0, 0, 73, 0, 42, 43, 45, + 47, 49, 51, 53, 55, 58, 65, 68, 72, 0, + 18, 11, 0, 0, 0, 0, 74, 75, 76, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, - 79, 0, 0, 14, 12, 45, 0, 47, 49, 51, - 53, 55, 56, 60, 61, 59, 58, 62, 63, 65, - 66, 69, 68, 70, 0, 0, 0, 0, 18, 0, - 79, 15, 13, 0, 0, 0, 20, 30, 82, 22, - 84, 0, 81, 80, 43, 21, 83, 0, 0, 16, - 29, 19, 31, 0, 23, 32, 26, 0, 76, 34, - 0, 0, 0, 0, 37, 36, 24, 35, 33, 0, - 77, 78, 25, 0, 28, 0, 0, 0, 27 + 80, 0, 0, 14, 12, 46, 0, 48, 50, 52, + 54, 56, 57, 61, 62, 60, 59, 63, 64, 66, + 67, 70, 69, 71, 0, 0, 0, 0, 19, 0, + 80, 15, 13, 0, 0, 0, 21, 31, 83, 23, + 85, 0, 82, 81, 44, 22, 84, 0, 0, 17, + 30, 20, 32, 0, 24, 33, 27, 0, 77, 35, + 0, 0, 0, 0, 38, 37, 25, 36, 34, 0, + 78, 79, 26, 0, 29, 0, 0, 0, 28 }; /* YYPGOTO[NTERM-NUM]. */ @@ -678,28 +678,28 @@ static const yytype_uint8 yystos[] = static const yytype_uint8 yyr1[] = { 0, 48, 49, 50, 50, 51, 51, 52, 52, 53, - 53, 54, 54, 54, 54, 54, 55, 56, 56, 57, - 57, 57, 57, 58, 58, 58, 58, 58, 58, 58, - 59, 59, 59, 60, 60, 60, 60, 60, 61, 61, - 61, 62, 63, 63, 64, 64, 65, 65, 66, 66, - 67, 67, 68, 68, 69, 69, 69, 70, 70, 70, - 70, 70, 71, 71, 71, 72, 72, 72, 73, 73, - 73, 73, 74, 74, 74, 74, 75, 75, 75, 76, - 76, 76, 77, 77, 77 + 53, 54, 54, 54, 54, 54, 54, 55, 56, 56, + 57, 57, 57, 57, 58, 58, 58, 58, 58, 58, + 58, 59, 59, 59, 60, 60, 60, 60, 60, 61, + 61, 61, 62, 63, 63, 64, 64, 65, 65, 66, + 66, 67, 67, 68, 68, 69, 69, 69, 70, 70, + 70, 70, 70, 71, 71, 71, 72, 72, 72, 73, + 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, + 76, 76, 76, 77, 77, 77 }; /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 3, 2, 4, 1, 2, 0, 2, 4, - 2, 2, 3, 4, 3, 4, 5, 0, 2, 4, - 2, 3, 2, 2, 3, 4, 2, 9, 5, 2, - 0, 2, 2, 3, 1, 2, 2, 2, 1, 1, - 3, 1, 1, 5, 1, 3, 1, 3, 1, 3, - 1, 3, 1, 3, 1, 3, 3, 1, 3, 3, - 3, 3, 3, 3, 1, 3, 3, 1, 3, 3, - 3, 1, 1, 2, 2, 2, 0, 2, 2, 0, - 2, 2, 2, 3, 2 + 2, 2, 3, 4, 3, 4, 0, 5, 0, 2, + 4, 2, 3, 2, 2, 3, 4, 2, 9, 5, + 2, 0, 2, 2, 3, 1, 2, 2, 2, 1, + 1, 3, 1, 1, 5, 1, 3, 1, 3, 1, + 3, 1, 3, 1, 3, 1, 3, 3, 1, 3, + 3, 3, 3, 3, 3, 1, 3, 3, 1, 3, + 3, 3, 1, 1, 2, 2, 2, 0, 2, 2, + 0, 2, 2, 2, 3, 2 }; @@ -1572,17 +1572,26 @@ yyreduce: { struct node *target = get_node_by_ref((yyvsp[-2].node), (yyvsp[-1].labelref)); - if (target) + if (target) { merge_nodes(target, (yyvsp[0].node)); - else - ERROR(&(yylsp[-1]), "Label or path %s not found", (yyvsp[-1].labelref)); + } else { + /* + * We rely on the rule being always: + * versioninfo plugindecl memreserves devicetree + * so $-1 is what we want (plugindecl) + */ + if ((yyvsp[(-1) - (3)].flags) & DTSF_PLUGIN) + add_orphan_node((yyvsp[-2].node), (yyvsp[0].node), (yyvsp[-1].labelref)); + else + ERROR(&(yylsp[-1]), "Label or path %s not found", (yyvsp[-1].labelref)); + } (yyval.node) = (yyvsp[-2].node); } -#line 1582 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1591 "dtc-parser.tab.c" /* yacc.c:1646 */ break; case 15: -#line 192 "dtc-parser.y" /* yacc.c:1646 */ +#line 201 "dtc-parser.y" /* yacc.c:1646 */ { struct node *target = get_node_by_ref((yyvsp[-3].node), (yyvsp[-1].labelref)); @@ -1594,100 +1603,109 @@ yyreduce: (yyval.node) = (yyvsp[-3].node); } -#line 1598 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1607 "dtc-parser.tab.c" /* yacc.c:1646 */ break; case 16: -#line 207 "dtc-parser.y" /* yacc.c:1646 */ +#line 213 "dtc-parser.y" /* yacc.c:1646 */ { - (yyval.node) = build_node((yyvsp[-3].proplist), (yyvsp[-2].nodelist)); + /* build empty node */ + (yyval.node) = name_node(build_node(NULL, NULL), ""); } -#line 1606 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1616 "dtc-parser.tab.c" /* yacc.c:1646 */ break; case 17: -#line 214 "dtc-parser.y" /* yacc.c:1646 */ +#line 221 "dtc-parser.y" /* yacc.c:1646 */ { - (yyval.proplist) = NULL; + (yyval.node) = build_node((yyvsp[-3].proplist), (yyvsp[-2].nodelist)); } -#line 1614 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1624 "dtc-parser.tab.c" /* yacc.c:1646 */ break; case 18: -#line 218 "dtc-parser.y" /* yacc.c:1646 */ +#line 228 "dtc-parser.y" /* yacc.c:1646 */ { - (yyval.proplist) = chain_property((yyvsp[0].prop), (yyvsp[-1].proplist)); + (yyval.proplist) = NULL; } -#line 1622 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1632 "dtc-parser.tab.c" /* yacc.c:1646 */ break; case 19: -#line 225 "dtc-parser.y" /* yacc.c:1646 */ +#line 232 "dtc-parser.y" /* yacc.c:1646 */ { - (yyval.prop) = build_property((yyvsp[-3].propnodename), (yyvsp[-1].data)); + (yyval.proplist) = chain_property((yyvsp[0].prop), (yyvsp[-1].proplist)); } -#line 1630 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1640 "dtc-parser.tab.c" /* yacc.c:1646 */ break; case 20: -#line 229 "dtc-parser.y" /* yacc.c:1646 */ +#line 239 "dtc-parser.y" /* yacc.c:1646 */ { - (yyval.prop) = build_property((yyvsp[-1].propnodename), empty_data); + (yyval.prop) = build_property((yyvsp[-3].propnodename), (yyvsp[-1].data)); } -#line 1638 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1648 "dtc-parser.tab.c" /* yacc.c:1646 */ break; case 21: -#line 233 "dtc-parser.y" /* yacc.c:1646 */ +#line 243 "dtc-parser.y" /* yacc.c:1646 */ { - (yyval.prop) = build_property_delete((yyvsp[-1].propnodename)); + (yyval.prop) = build_property((yyvsp[-1].propnodename), empty_data); } -#line 1646 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1656 "dtc-parser.tab.c" /* yacc.c:1646 */ break; case 22: -#line 237 "dtc-parser.y" /* yacc.c:1646 */ +#line 247 "dtc-parser.y" /* yacc.c:1646 */ + { + (yyval.prop) = build_property_delete((yyvsp[-1].propnodename)); + } +#line 1664 "dtc-parser.tab.c" /* yacc.c:1646 */ + break; + + case 23: +#line 251 "dtc-parser.y" /* yacc.c:1646 */ { add_label(&(yyvsp[0].prop)->labels, (yyvsp[-1].labelref)); (yyval.prop) = (yyvsp[0].prop); } -#line 1655 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1673 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 23: -#line 245 "dtc-parser.y" /* yacc.c:1646 */ + case 24: +#line 259 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.data) = data_merge((yyvsp[-1].data), (yyvsp[0].data)); } -#line 1663 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1681 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 24: -#line 249 "dtc-parser.y" /* yacc.c:1646 */ + case 25: +#line 263 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.data) = data_merge((yyvsp[-2].data), (yyvsp[-1].array).data); } -#line 1671 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1689 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 25: -#line 253 "dtc-parser.y" /* yacc.c:1646 */ + case 26: +#line 267 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.data) = data_merge((yyvsp[-3].data), (yyvsp[-1].data)); } -#line 1679 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1697 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 26: -#line 257 "dtc-parser.y" /* yacc.c:1646 */ + case 27: +#line 271 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.data) = data_add_marker((yyvsp[-1].data), REF_PATH, (yyvsp[0].labelref)); } -#line 1687 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1705 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 27: -#line 261 "dtc-parser.y" /* yacc.c:1646 */ + case 28: +#line 275 "dtc-parser.y" /* yacc.c:1646 */ { FILE *f = srcfile_relative_open((yyvsp[-5].data).val, NULL); struct data d; @@ -1703,11 +1721,11 @@ yyreduce: (yyval.data) = data_merge((yyvsp[-8].data), d); fclose(f); } -#line 1707 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1725 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 28: -#line 277 "dtc-parser.y" /* yacc.c:1646 */ + case 29: +#line 291 "dtc-parser.y" /* yacc.c:1646 */ { FILE *f = srcfile_relative_open((yyvsp[-1].data).val, NULL); struct data d = empty_data; @@ -1717,43 +1735,43 @@ yyreduce: (yyval.data) = data_merge((yyvsp[-4].data), d); fclose(f); } -#line 1721 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1739 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 29: -#line 287 "dtc-parser.y" /* yacc.c:1646 */ + case 30: +#line 301 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.data) = data_add_marker((yyvsp[-1].data), LABEL, (yyvsp[0].labelref)); } -#line 1729 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1747 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 30: -#line 294 "dtc-parser.y" /* yacc.c:1646 */ + case 31: +#line 308 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.data) = empty_data; } -#line 1737 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1755 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 31: -#line 298 "dtc-parser.y" /* yacc.c:1646 */ + case 32: +#line 312 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.data) = (yyvsp[-1].data); } -#line 1745 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1763 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 32: -#line 302 "dtc-parser.y" /* yacc.c:1646 */ + case 33: +#line 316 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.data) = data_add_marker((yyvsp[-1].data), LABEL, (yyvsp[0].labelref)); } -#line 1753 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1771 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 33: -#line 309 "dtc-parser.y" /* yacc.c:1646 */ + case 34: +#line 323 "dtc-parser.y" /* yacc.c:1646 */ { unsigned long long bits; @@ -1769,20 +1787,20 @@ yyreduce: (yyval.array).data = empty_data; (yyval.array).bits = bits; } -#line 1773 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1791 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 34: -#line 325 "dtc-parser.y" /* yacc.c:1646 */ + case 35: +#line 339 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.array).data = empty_data; (yyval.array).bits = 32; } -#line 1782 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1800 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 35: -#line 330 "dtc-parser.y" /* yacc.c:1646 */ + case 36: +#line 344 "dtc-parser.y" /* yacc.c:1646 */ { if ((yyvsp[-1].array).bits < 64) { uint64_t mask = (1ULL << (yyvsp[-1].array).bits) - 1; @@ -1801,11 +1819,11 @@ yyreduce: (yyval.array).data = data_append_integer((yyvsp[-1].array).data, (yyvsp[0].integer), (yyvsp[-1].array).bits); } -#line 1805 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1823 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 36: -#line 349 "dtc-parser.y" /* yacc.c:1646 */ + case 37: +#line 363 "dtc-parser.y" /* yacc.c:1646 */ { uint64_t val = ~0ULL >> (64 - (yyvsp[-1].array).bits); @@ -1819,129 +1837,129 @@ yyreduce: (yyval.array).data = data_append_integer((yyvsp[-1].array).data, val, (yyvsp[-1].array).bits); } -#line 1823 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1841 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 37: -#line 363 "dtc-parser.y" /* yacc.c:1646 */ + case 38: +#line 377 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.array).data = data_add_marker((yyvsp[-1].array).data, LABEL, (yyvsp[0].labelref)); } -#line 1831 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1849 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 40: -#line 372 "dtc-parser.y" /* yacc.c:1646 */ + case 41: +#line 386 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = (yyvsp[-1].integer); } -#line 1839 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1857 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 43: -#line 383 "dtc-parser.y" /* yacc.c:1646 */ + case 44: +#line 397 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = (yyvsp[-4].integer) ? (yyvsp[-2].integer) : (yyvsp[0].integer); } -#line 1845 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1863 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 45: -#line 388 "dtc-parser.y" /* yacc.c:1646 */ + case 46: +#line 402 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = (yyvsp[-2].integer) || (yyvsp[0].integer); } -#line 1851 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1869 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 47: -#line 393 "dtc-parser.y" /* yacc.c:1646 */ + case 48: +#line 407 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = (yyvsp[-2].integer) && (yyvsp[0].integer); } -#line 1857 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1875 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 49: -#line 398 "dtc-parser.y" /* yacc.c:1646 */ + case 50: +#line 412 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = (yyvsp[-2].integer) | (yyvsp[0].integer); } -#line 1863 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1881 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 51: -#line 403 "dtc-parser.y" /* yacc.c:1646 */ + case 52: +#line 417 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = (yyvsp[-2].integer) ^ (yyvsp[0].integer); } -#line 1869 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1887 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 53: -#line 408 "dtc-parser.y" /* yacc.c:1646 */ + case 54: +#line 422 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = (yyvsp[-2].integer) & (yyvsp[0].integer); } -#line 1875 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1893 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 55: -#line 413 "dtc-parser.y" /* yacc.c:1646 */ + case 56: +#line 427 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = (yyvsp[-2].integer) == (yyvsp[0].integer); } -#line 1881 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1899 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 56: -#line 414 "dtc-parser.y" /* yacc.c:1646 */ + case 57: +#line 428 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = (yyvsp[-2].integer) != (yyvsp[0].integer); } -#line 1887 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1905 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 58: -#line 419 "dtc-parser.y" /* yacc.c:1646 */ + case 59: +#line 433 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = (yyvsp[-2].integer) < (yyvsp[0].integer); } -#line 1893 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1911 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 59: -#line 420 "dtc-parser.y" /* yacc.c:1646 */ + case 60: +#line 434 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = (yyvsp[-2].integer) > (yyvsp[0].integer); } -#line 1899 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1917 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 60: -#line 421 "dtc-parser.y" /* yacc.c:1646 */ + case 61: +#line 435 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = (yyvsp[-2].integer) <= (yyvsp[0].integer); } -#line 1905 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1923 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 61: -#line 422 "dtc-parser.y" /* yacc.c:1646 */ + case 62: +#line 436 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = (yyvsp[-2].integer) >= (yyvsp[0].integer); } -#line 1911 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1929 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 62: -#line 426 "dtc-parser.y" /* yacc.c:1646 */ + case 63: +#line 440 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = (yyvsp[-2].integer) << (yyvsp[0].integer); } -#line 1917 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1935 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 63: -#line 427 "dtc-parser.y" /* yacc.c:1646 */ + case 64: +#line 441 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = (yyvsp[-2].integer) >> (yyvsp[0].integer); } -#line 1923 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1941 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 65: -#line 432 "dtc-parser.y" /* yacc.c:1646 */ + case 66: +#line 446 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = (yyvsp[-2].integer) + (yyvsp[0].integer); } -#line 1929 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1947 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 66: -#line 433 "dtc-parser.y" /* yacc.c:1646 */ + case 67: +#line 447 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = (yyvsp[-2].integer) - (yyvsp[0].integer); } -#line 1935 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1953 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 68: -#line 438 "dtc-parser.y" /* yacc.c:1646 */ + case 69: +#line 452 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = (yyvsp[-2].integer) * (yyvsp[0].integer); } -#line 1941 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1959 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 69: -#line 440 "dtc-parser.y" /* yacc.c:1646 */ + case 70: +#line 454 "dtc-parser.y" /* yacc.c:1646 */ { if ((yyvsp[0].integer) != 0) { (yyval.integer) = (yyvsp[-2].integer) / (yyvsp[0].integer); @@ -1950,11 +1968,11 @@ yyreduce: (yyval.integer) = 0; } } -#line 1954 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1972 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 70: -#line 449 "dtc-parser.y" /* yacc.c:1646 */ + case 71: +#line 463 "dtc-parser.y" /* yacc.c:1646 */ { if ((yyvsp[0].integer) != 0) { (yyval.integer) = (yyvsp[-2].integer) % (yyvsp[0].integer); @@ -1963,103 +1981,103 @@ yyreduce: (yyval.integer) = 0; } } -#line 1967 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1985 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 73: -#line 462 "dtc-parser.y" /* yacc.c:1646 */ + case 74: +#line 476 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = -(yyvsp[0].integer); } -#line 1973 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1991 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 74: -#line 463 "dtc-parser.y" /* yacc.c:1646 */ + case 75: +#line 477 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = ~(yyvsp[0].integer); } -#line 1979 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 1997 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 75: -#line 464 "dtc-parser.y" /* yacc.c:1646 */ + case 76: +#line 478 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.integer) = !(yyvsp[0].integer); } -#line 1985 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 2003 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 76: -#line 469 "dtc-parser.y" /* yacc.c:1646 */ + case 77: +#line 483 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.data) = empty_data; } -#line 1993 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 2011 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 77: -#line 473 "dtc-parser.y" /* yacc.c:1646 */ + case 78: +#line 487 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.data) = data_append_byte((yyvsp[-1].data), (yyvsp[0].byte)); } -#line 2001 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 2019 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 78: -#line 477 "dtc-parser.y" /* yacc.c:1646 */ + case 79: +#line 491 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.data) = data_add_marker((yyvsp[-1].data), LABEL, (yyvsp[0].labelref)); } -#line 2009 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 2027 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 79: -#line 484 "dtc-parser.y" /* yacc.c:1646 */ + case 80: +#line 498 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.nodelist) = NULL; } -#line 2017 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 2035 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 80: -#line 488 "dtc-parser.y" /* yacc.c:1646 */ + case 81: +#line 502 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.nodelist) = chain_node((yyvsp[-1].node), (yyvsp[0].nodelist)); } -#line 2025 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 2043 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 81: -#line 492 "dtc-parser.y" /* yacc.c:1646 */ + case 82: +#line 506 "dtc-parser.y" /* yacc.c:1646 */ { ERROR(&(yylsp[0]), "Properties must precede subnodes"); YYERROR; } -#line 2034 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 2052 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 82: -#line 500 "dtc-parser.y" /* yacc.c:1646 */ + case 83: +#line 514 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.node) = name_node((yyvsp[0].node), (yyvsp[-1].propnodename)); } -#line 2042 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 2060 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 83: -#line 504 "dtc-parser.y" /* yacc.c:1646 */ + case 84: +#line 518 "dtc-parser.y" /* yacc.c:1646 */ { (yyval.node) = name_node(build_node_delete(), (yyvsp[-1].propnodename)); } -#line 2050 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 2068 "dtc-parser.tab.c" /* yacc.c:1646 */ break; - case 84: -#line 508 "dtc-parser.y" /* yacc.c:1646 */ + case 85: +#line 522 "dtc-parser.y" /* yacc.c:1646 */ { add_label(&(yyvsp[0].node)->labels, (yyvsp[-1].labelref)); (yyval.node) = (yyvsp[0].node); } -#line 2059 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 2077 "dtc-parser.tab.c" /* yacc.c:1646 */ break; -#line 2063 "dtc-parser.tab.c" /* yacc.c:1646 */ +#line 2081 "dtc-parser.tab.c" /* yacc.c:1646 */ default: break; } /* User semantic actions sometimes alter yychar, and that requires @@ -2294,7 +2312,7 @@ yyreturn: #endif return yyresult; } -#line 514 "dtc-parser.y" /* yacc.c:1906 */ +#line 528 "dtc-parser.y" /* yacc.c:1906 */ void yyerror(char const *s) diff --git a/scripts/dtc/dtc-parser.y b/scripts/dtc/dtc-parser.y index ca3f5003427c..affc81a8f9ab 100644 --- a/scripts/dtc/dtc-parser.y +++ b/scripts/dtc/dtc-parser.y @@ -182,10 +182,19 @@ devicetree: { struct node *target = get_node_by_ref($1, $2); - if (target) + if (target) { merge_nodes(target, $3); - else - ERROR(&@2, "Label or path %s not found", $2); + } else { + /* + * We rely on the rule being always: + * versioninfo plugindecl memreserves devicetree + * so $-1 is what we want (plugindecl) + */ + if ($-1 & DTSF_PLUGIN) + add_orphan_node($1, $3, $2); + else + ERROR(&@2, "Label or path %s not found", $2); + } $$ = $1; } | devicetree DT_DEL_NODE DT_REF ';' @@ -200,6 +209,11 @@ devicetree: $$ = $1; } + | /* empty */ + { + /* build empty node */ + $$ = name_node(build_node(NULL, NULL), ""); + } ; nodedef: diff --git a/scripts/dtc/dtc.c b/scripts/dtc/dtc.c index f5eed9d72c02..5ed873c72ad1 100644 --- a/scripts/dtc/dtc.c +++ b/scripts/dtc/dtc.c @@ -31,7 +31,7 @@ int reservenum; /* Number of memory reservation slots */ int minsize; /* Minimum blob size */ int padsize; /* Additional padding to blob */ int alignsize; /* Additional padding to blob accroding to the alignsize */ -int phandle_format = PHANDLE_BOTH; /* Use linux,phandle or phandle properties */ +int phandle_format = PHANDLE_EPAPR; /* Use linux,phandle or phandle properties */ int generate_symbols; /* enable symbols & fixup support */ int generate_fixups; /* suppress generation of fixups on symbol support */ int auto_label_aliases; /* auto generate labels -> aliases */ diff --git a/scripts/dtc/dtc.h b/scripts/dtc/dtc.h index fc24e17510fd..35cf926cc14a 100644 --- a/scripts/dtc/dtc.h +++ b/scripts/dtc/dtc.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -202,6 +203,7 @@ struct node *build_node_delete(void); struct node *name_node(struct node *node, char *name); struct node *chain_node(struct node *first, struct node *list); struct node *merge_nodes(struct node *old_node, struct node *new_node); +void add_orphan_node(struct node *old_node, struct node *new_node, char *ref); void add_property(struct node *node, struct property *prop); void delete_property_by_name(struct node *node, char *name); @@ -215,6 +217,7 @@ void append_to_property(struct node *node, const char *get_unitname(struct node *node); struct property *get_property(struct node *node, const char *propname); cell_t propval_cell(struct property *prop); +cell_t propval_cell_n(struct property *prop, int n); struct property *get_property_by_label(struct node *tree, const char *label, struct node **node); struct marker *get_marker_label(struct node *tree, const char *label, diff --git a/scripts/dtc/libfdt/fdt_addresses.c b/scripts/dtc/libfdt/fdt_addresses.c new file mode 100644 index 000000000000..eff4dbcc729d --- /dev/null +++ b/scripts/dtc/libfdt/fdt_addresses.c @@ -0,0 +1,96 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2014 David Gibson + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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 (at your option) any later version. + * + * This library 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 library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +int fdt_address_cells(const void *fdt, int nodeoffset) +{ + const fdt32_t *ac; + int val; + int len; + + ac = fdt_getprop(fdt, nodeoffset, "#address-cells", &len); + if (!ac) + return 2; + + if (len != sizeof(*ac)) + return -FDT_ERR_BADNCELLS; + + val = fdt32_to_cpu(*ac); + if ((val <= 0) || (val > FDT_MAX_NCELLS)) + return -FDT_ERR_BADNCELLS; + + return val; +} + +int fdt_size_cells(const void *fdt, int nodeoffset) +{ + const fdt32_t *sc; + int val; + int len; + + sc = fdt_getprop(fdt, nodeoffset, "#size-cells", &len); + if (!sc) + return 2; + + if (len != sizeof(*sc)) + return -FDT_ERR_BADNCELLS; + + val = fdt32_to_cpu(*sc); + if ((val < 0) || (val > FDT_MAX_NCELLS)) + return -FDT_ERR_BADNCELLS; + + return val; +} diff --git a/scripts/dtc/libfdt/fdt_empty_tree.c b/scripts/dtc/libfdt/fdt_empty_tree.c index f72d13b1d19c..f2ae9b77c285 100644 --- a/scripts/dtc/libfdt/fdt_empty_tree.c +++ b/scripts/dtc/libfdt/fdt_empty_tree.c @@ -81,4 +81,3 @@ int fdt_create_empty_tree(void *buf, int bufsize) return fdt_open_into(buf, buf, bufsize); } - diff --git a/scripts/dtc/libfdt/fdt_overlay.c b/scripts/dtc/libfdt/fdt_overlay.c new file mode 100644 index 000000000000..bd81241e6658 --- /dev/null +++ b/scripts/dtc/libfdt/fdt_overlay.c @@ -0,0 +1,861 @@ +#include "libfdt_env.h" + +#include +#include + +#include "libfdt_internal.h" + +/** + * overlay_get_target_phandle - retrieves the target phandle of a fragment + * @fdto: pointer to the device tree overlay blob + * @fragment: node offset of the fragment in the overlay + * + * overlay_get_target_phandle() retrieves the target phandle of an + * overlay fragment when that fragment uses a phandle (target + * property) instead of a path (target-path property). + * + * returns: + * the phandle pointed by the target property + * 0, if the phandle was not found + * -1, if the phandle was malformed + */ +static uint32_t overlay_get_target_phandle(const void *fdto, int fragment) +{ + const fdt32_t *val; + int len; + + val = fdt_getprop(fdto, fragment, "target", &len); + if (!val) + return 0; + + if ((len != sizeof(*val)) || (fdt32_to_cpu(*val) == (uint32_t)-1)) + return (uint32_t)-1; + + return fdt32_to_cpu(*val); +} + +/** + * overlay_get_target - retrieves the offset of a fragment's target + * @fdt: Base device tree blob + * @fdto: Device tree overlay blob + * @fragment: node offset of the fragment in the overlay + * @pathp: pointer which receives the path of the target (or NULL) + * + * overlay_get_target() retrieves the target offset in the base + * device tree of a fragment, no matter how the actual targetting is + * done (through a phandle or a path) + * + * returns: + * the targetted node offset in the base device tree + * Negative error code on error + */ +static int overlay_get_target(const void *fdt, const void *fdto, + int fragment, char const **pathp) +{ + uint32_t phandle; + const char *path = NULL; + int path_len = 0, ret; + + /* Try first to do a phandle based lookup */ + phandle = overlay_get_target_phandle(fdto, fragment); + if (phandle == (uint32_t)-1) + return -FDT_ERR_BADPHANDLE; + + /* no phandle, try path */ + if (!phandle) { + /* And then a path based lookup */ + path = fdt_getprop(fdto, fragment, "target-path", &path_len); + if (path) + ret = fdt_path_offset(fdt, path); + else + ret = path_len; + } else + ret = fdt_node_offset_by_phandle(fdt, phandle); + + /* + * If we haven't found either a target or a + * target-path property in a node that contains a + * __overlay__ subnode (we wouldn't be called + * otherwise), consider it a improperly written + * overlay + */ + if (ret < 0 && path_len == -FDT_ERR_NOTFOUND) + ret = -FDT_ERR_BADOVERLAY; + + /* return on error */ + if (ret < 0) + return ret; + + /* return pointer to path (if available) */ + if (pathp) + *pathp = path ? path : NULL; + + return ret; +} + +/** + * overlay_phandle_add_offset - Increases a phandle by an offset + * @fdt: Base device tree blob + * @node: Device tree overlay blob + * @name: Name of the property to modify (phandle or linux,phandle) + * @delta: offset to apply + * + * overlay_phandle_add_offset() increments a node phandle by a given + * offset. + * + * returns: + * 0 on success. + * Negative error code on error + */ +static int overlay_phandle_add_offset(void *fdt, int node, + const char *name, uint32_t delta) +{ + const fdt32_t *val; + uint32_t adj_val; + int len; + + val = fdt_getprop(fdt, node, name, &len); + if (!val) + return len; + + if (len != sizeof(*val)) + return -FDT_ERR_BADPHANDLE; + + adj_val = fdt32_to_cpu(*val); + if ((adj_val + delta) < adj_val) + return -FDT_ERR_NOPHANDLES; + + adj_val += delta; + if (adj_val == (uint32_t)-1) + return -FDT_ERR_NOPHANDLES; + + return fdt_setprop_inplace_u32(fdt, node, name, adj_val); +} + +/** + * overlay_adjust_node_phandles - Offsets the phandles of a node + * @fdto: Device tree overlay blob + * @node: Offset of the node we want to adjust + * @delta: Offset to shift the phandles of + * + * overlay_adjust_node_phandles() adds a constant to all the phandles + * of a given node. This is mainly use as part of the overlay + * application process, when we want to update all the overlay + * phandles to not conflict with the overlays of the base device tree. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_adjust_node_phandles(void *fdto, int node, + uint32_t delta) +{ + int child; + int ret; + + ret = overlay_phandle_add_offset(fdto, node, "phandle", delta); + if (ret && ret != -FDT_ERR_NOTFOUND) + return ret; + + ret = overlay_phandle_add_offset(fdto, node, "linux,phandle", delta); + if (ret && ret != -FDT_ERR_NOTFOUND) + return ret; + + fdt_for_each_subnode(child, fdto, node) { + ret = overlay_adjust_node_phandles(fdto, child, delta); + if (ret) + return ret; + } + + return 0; +} + +/** + * overlay_adjust_local_phandles - Adjust the phandles of a whole overlay + * @fdto: Device tree overlay blob + * @delta: Offset to shift the phandles of + * + * overlay_adjust_local_phandles() adds a constant to all the + * phandles of an overlay. This is mainly use as part of the overlay + * application process, when we want to update all the overlay + * phandles to not conflict with the overlays of the base device tree. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_adjust_local_phandles(void *fdto, uint32_t delta) +{ + /* + * Start adjusting the phandles from the overlay root + */ + return overlay_adjust_node_phandles(fdto, 0, delta); +} + +/** + * overlay_update_local_node_references - Adjust the overlay references + * @fdto: Device tree overlay blob + * @tree_node: Node offset of the node to operate on + * @fixup_node: Node offset of the matching local fixups node + * @delta: Offset to shift the phandles of + * + * overlay_update_local_nodes_references() update the phandles + * pointing to a node within the device tree overlay by adding a + * constant delta. + * + * This is mainly used as part of a device tree application process, + * where you want the device tree overlays phandles to not conflict + * with the ones from the base device tree before merging them. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_update_local_node_references(void *fdto, + int tree_node, + int fixup_node, + uint32_t delta) +{ + int fixup_prop; + int fixup_child; + int ret; + + fdt_for_each_property_offset(fixup_prop, fdto, fixup_node) { + const fdt32_t *fixup_val; + const char *tree_val; + const char *name; + int fixup_len; + int tree_len; + int i; + + fixup_val = fdt_getprop_by_offset(fdto, fixup_prop, + &name, &fixup_len); + if (!fixup_val) + return fixup_len; + + if (fixup_len % sizeof(uint32_t)) + return -FDT_ERR_BADOVERLAY; + + tree_val = fdt_getprop(fdto, tree_node, name, &tree_len); + if (!tree_val) { + if (tree_len == -FDT_ERR_NOTFOUND) + return -FDT_ERR_BADOVERLAY; + + return tree_len; + } + + for (i = 0; i < (fixup_len / sizeof(uint32_t)); i++) { + fdt32_t adj_val; + uint32_t poffset; + + poffset = fdt32_to_cpu(fixup_val[i]); + + /* + * phandles to fixup can be unaligned. + * + * Use a memcpy for the architectures that do + * not support unaligned accesses. + */ + memcpy(&adj_val, tree_val + poffset, sizeof(adj_val)); + + adj_val = cpu_to_fdt32(fdt32_to_cpu(adj_val) + delta); + + ret = fdt_setprop_inplace_namelen_partial(fdto, + tree_node, + name, + strlen(name), + poffset, + &adj_val, + sizeof(adj_val)); + if (ret == -FDT_ERR_NOSPACE) + return -FDT_ERR_BADOVERLAY; + + if (ret) + return ret; + } + } + + fdt_for_each_subnode(fixup_child, fdto, fixup_node) { + const char *fixup_child_name = fdt_get_name(fdto, fixup_child, + NULL); + int tree_child; + + tree_child = fdt_subnode_offset(fdto, tree_node, + fixup_child_name); + if (tree_child == -FDT_ERR_NOTFOUND) + return -FDT_ERR_BADOVERLAY; + if (tree_child < 0) + return tree_child; + + ret = overlay_update_local_node_references(fdto, + tree_child, + fixup_child, + delta); + if (ret) + return ret; + } + + return 0; +} + +/** + * overlay_update_local_references - Adjust the overlay references + * @fdto: Device tree overlay blob + * @delta: Offset to shift the phandles of + * + * overlay_update_local_references() update all the phandles pointing + * to a node within the device tree overlay by adding a constant + * delta to not conflict with the base overlay. + * + * This is mainly used as part of a device tree application process, + * where you want the device tree overlays phandles to not conflict + * with the ones from the base device tree before merging them. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_update_local_references(void *fdto, uint32_t delta) +{ + int fixups; + + fixups = fdt_path_offset(fdto, "/__local_fixups__"); + if (fixups < 0) { + /* There's no local phandles to adjust, bail out */ + if (fixups == -FDT_ERR_NOTFOUND) + return 0; + + return fixups; + } + + /* + * Update our local references from the root of the tree + */ + return overlay_update_local_node_references(fdto, 0, fixups, + delta); +} + +/** + * overlay_fixup_one_phandle - Set an overlay phandle to the base one + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * @symbols_off: Node offset of the symbols node in the base device tree + * @path: Path to a node holding a phandle in the overlay + * @path_len: number of path characters to consider + * @name: Name of the property holding the phandle reference in the overlay + * @name_len: number of name characters to consider + * @poffset: Offset within the overlay property where the phandle is stored + * @label: Label of the node referenced by the phandle + * + * overlay_fixup_one_phandle() resolves an overlay phandle pointing to + * a node in the base device tree. + * + * This is part of the device tree overlay application process, when + * you want all the phandles in the overlay to point to the actual + * base dt nodes. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_fixup_one_phandle(void *fdt, void *fdto, + int symbols_off, + const char *path, uint32_t path_len, + const char *name, uint32_t name_len, + int poffset, const char *label) +{ + const char *symbol_path; + uint32_t phandle; + fdt32_t phandle_prop; + int symbol_off, fixup_off; + int prop_len; + + if (symbols_off < 0) + return symbols_off; + + symbol_path = fdt_getprop(fdt, symbols_off, label, + &prop_len); + if (!symbol_path) + return prop_len; + + symbol_off = fdt_path_offset(fdt, symbol_path); + if (symbol_off < 0) + return symbol_off; + + phandle = fdt_get_phandle(fdt, symbol_off); + if (!phandle) + return -FDT_ERR_NOTFOUND; + + fixup_off = fdt_path_offset_namelen(fdto, path, path_len); + if (fixup_off == -FDT_ERR_NOTFOUND) + return -FDT_ERR_BADOVERLAY; + if (fixup_off < 0) + return fixup_off; + + phandle_prop = cpu_to_fdt32(phandle); + return fdt_setprop_inplace_namelen_partial(fdto, fixup_off, + name, name_len, poffset, + &phandle_prop, + sizeof(phandle_prop)); +}; + +/** + * overlay_fixup_phandle - Set an overlay phandle to the base one + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * @symbols_off: Node offset of the symbols node in the base device tree + * @property: Property offset in the overlay holding the list of fixups + * + * overlay_fixup_phandle() resolves all the overlay phandles pointed + * to in a __fixups__ property, and updates them to match the phandles + * in use in the base device tree. + * + * This is part of the device tree overlay application process, when + * you want all the phandles in the overlay to point to the actual + * base dt nodes. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_fixup_phandle(void *fdt, void *fdto, int symbols_off, + int property) +{ + const char *value; + const char *label; + int len; + + value = fdt_getprop_by_offset(fdto, property, + &label, &len); + if (!value) { + if (len == -FDT_ERR_NOTFOUND) + return -FDT_ERR_INTERNAL; + + return len; + } + + do { + const char *path, *name, *fixup_end; + const char *fixup_str = value; + uint32_t path_len, name_len; + uint32_t fixup_len; + char *sep, *endptr; + int poffset, ret; + + fixup_end = memchr(value, '\0', len); + if (!fixup_end) + return -FDT_ERR_BADOVERLAY; + fixup_len = fixup_end - fixup_str; + + len -= fixup_len + 1; + value += fixup_len + 1; + + path = fixup_str; + sep = memchr(fixup_str, ':', fixup_len); + if (!sep || *sep != ':') + return -FDT_ERR_BADOVERLAY; + + path_len = sep - path; + if (path_len == (fixup_len - 1)) + return -FDT_ERR_BADOVERLAY; + + fixup_len -= path_len + 1; + name = sep + 1; + sep = memchr(name, ':', fixup_len); + if (!sep || *sep != ':') + return -FDT_ERR_BADOVERLAY; + + name_len = sep - name; + if (!name_len) + return -FDT_ERR_BADOVERLAY; + + poffset = strtoul(sep + 1, &endptr, 10); + if ((*endptr != '\0') || (endptr <= (sep + 1))) + return -FDT_ERR_BADOVERLAY; + + ret = overlay_fixup_one_phandle(fdt, fdto, symbols_off, + path, path_len, name, name_len, + poffset, label); + if (ret) + return ret; + } while (len > 0); + + return 0; +} + +/** + * overlay_fixup_phandles - Resolve the overlay phandles to the base + * device tree + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * + * overlay_fixup_phandles() resolves all the overlay phandles pointing + * to nodes in the base device tree. + * + * This is one of the steps of the device tree overlay application + * process, when you want all the phandles in the overlay to point to + * the actual base dt nodes. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_fixup_phandles(void *fdt, void *fdto) +{ + int fixups_off, symbols_off; + int property; + + /* We can have overlays without any fixups */ + fixups_off = fdt_path_offset(fdto, "/__fixups__"); + if (fixups_off == -FDT_ERR_NOTFOUND) + return 0; /* nothing to do */ + if (fixups_off < 0) + return fixups_off; + + /* And base DTs without symbols */ + symbols_off = fdt_path_offset(fdt, "/__symbols__"); + if ((symbols_off < 0 && (symbols_off != -FDT_ERR_NOTFOUND))) + return symbols_off; + + fdt_for_each_property_offset(property, fdto, fixups_off) { + int ret; + + ret = overlay_fixup_phandle(fdt, fdto, symbols_off, property); + if (ret) + return ret; + } + + return 0; +} + +/** + * overlay_apply_node - Merges a node into the base device tree + * @fdt: Base Device Tree blob + * @target: Node offset in the base device tree to apply the fragment to + * @fdto: Device tree overlay blob + * @node: Node offset in the overlay holding the changes to merge + * + * overlay_apply_node() merges a node into a target base device tree + * node pointed. + * + * This is part of the final step in the device tree overlay + * application process, when all the phandles have been adjusted and + * resolved and you just have to merge overlay into the base device + * tree. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_apply_node(void *fdt, int target, + void *fdto, int node) +{ + int property; + int subnode; + + fdt_for_each_property_offset(property, fdto, node) { + const char *name; + const void *prop; + int prop_len; + int ret; + + prop = fdt_getprop_by_offset(fdto, property, &name, + &prop_len); + if (prop_len == -FDT_ERR_NOTFOUND) + return -FDT_ERR_INTERNAL; + if (prop_len < 0) + return prop_len; + + ret = fdt_setprop(fdt, target, name, prop, prop_len); + if (ret) + return ret; + } + + fdt_for_each_subnode(subnode, fdto, node) { + const char *name = fdt_get_name(fdto, subnode, NULL); + int nnode; + int ret; + + nnode = fdt_add_subnode(fdt, target, name); + if (nnode == -FDT_ERR_EXISTS) { + nnode = fdt_subnode_offset(fdt, target, name); + if (nnode == -FDT_ERR_NOTFOUND) + return -FDT_ERR_INTERNAL; + } + + if (nnode < 0) + return nnode; + + ret = overlay_apply_node(fdt, nnode, fdto, subnode); + if (ret) + return ret; + } + + return 0; +} + +/** + * overlay_merge - Merge an overlay into its base device tree + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * + * overlay_merge() merges an overlay into its base device tree. + * + * This is the next to last step in the device tree overlay application + * process, when all the phandles have been adjusted and resolved and + * you just have to merge overlay into the base device tree. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_merge(void *fdt, void *fdto) +{ + int fragment; + + fdt_for_each_subnode(fragment, fdto, 0) { + int overlay; + int target; + int ret; + + /* + * Each fragments will have an __overlay__ node. If + * they don't, it's not supposed to be merged + */ + overlay = fdt_subnode_offset(fdto, fragment, "__overlay__"); + if (overlay == -FDT_ERR_NOTFOUND) + continue; + + if (overlay < 0) + return overlay; + + target = overlay_get_target(fdt, fdto, fragment, NULL); + if (target < 0) + return target; + + ret = overlay_apply_node(fdt, target, fdto, overlay); + if (ret) + return ret; + } + + return 0; +} + +static int get_path_len(const void *fdt, int nodeoffset) +{ + int len = 0, namelen; + const char *name; + + FDT_CHECK_HEADER(fdt); + + for (;;) { + name = fdt_get_name(fdt, nodeoffset, &namelen); + if (!name) + return namelen; + + /* root? we're done */ + if (namelen == 0) + break; + + nodeoffset = fdt_parent_offset(fdt, nodeoffset); + if (nodeoffset < 0) + return nodeoffset; + len += namelen + 1; + } + + /* in case of root pretend it's "/" */ + if (len == 0) + len++; + return len; +} + +/** + * overlay_symbol_update - Update the symbols of base tree after a merge + * @fdt: Base Device Tree blob + * @fdto: Device tree overlay blob + * + * overlay_symbol_update() updates the symbols of the base tree with the + * symbols of the applied overlay + * + * This is the last step in the device tree overlay application + * process, allowing the reference of overlay symbols by subsequent + * overlay operations. + * + * returns: + * 0 on success + * Negative error code on failure + */ +static int overlay_symbol_update(void *fdt, void *fdto) +{ + int root_sym, ov_sym, prop, path_len, fragment, target; + int len, frag_name_len, ret, rel_path_len; + const char *s, *e; + const char *path; + const char *name; + const char *frag_name; + const char *rel_path; + const char *target_path; + char *buf; + void *p; + + ov_sym = fdt_subnode_offset(fdto, 0, "__symbols__"); + + /* if no overlay symbols exist no problem */ + if (ov_sym < 0) + return 0; + + root_sym = fdt_subnode_offset(fdt, 0, "__symbols__"); + + /* it no root symbols exist we should create them */ + if (root_sym == -FDT_ERR_NOTFOUND) + root_sym = fdt_add_subnode(fdt, 0, "__symbols__"); + + /* any error is fatal now */ + if (root_sym < 0) + return root_sym; + + /* iterate over each overlay symbol */ + fdt_for_each_property_offset(prop, fdto, ov_sym) { + path = fdt_getprop_by_offset(fdto, prop, &name, &path_len); + if (!path) + return path_len; + + /* verify it's a string property (terminated by a single \0) */ + if (path_len < 1 || memchr(path, '\0', path_len) != &path[path_len - 1]) + return -FDT_ERR_BADVALUE; + + /* keep end marker to avoid strlen() */ + e = path + path_len; + + /* format: //__overlay__/ */ + + if (*path != '/') + return -FDT_ERR_BADVALUE; + + /* get fragment name first */ + s = strchr(path + 1, '/'); + if (!s) + return -FDT_ERR_BADOVERLAY; + + frag_name = path + 1; + frag_name_len = s - path - 1; + + /* verify format; safe since "s" lies in \0 terminated prop */ + len = sizeof("/__overlay__/") - 1; + if ((e - s) < len || memcmp(s, "/__overlay__/", len)) + return -FDT_ERR_BADOVERLAY; + + rel_path = s + len; + rel_path_len = e - rel_path; + + /* find the fragment index in which the symbol lies */ + ret = fdt_subnode_offset_namelen(fdto, 0, frag_name, + frag_name_len); + /* not found? */ + if (ret < 0) + return -FDT_ERR_BADOVERLAY; + fragment = ret; + + /* an __overlay__ subnode must exist */ + ret = fdt_subnode_offset(fdto, fragment, "__overlay__"); + if (ret < 0) + return -FDT_ERR_BADOVERLAY; + + /* get the target of the fragment */ + ret = overlay_get_target(fdt, fdto, fragment, &target_path); + if (ret < 0) + return ret; + target = ret; + + /* if we have a target path use */ + if (!target_path) { + ret = get_path_len(fdt, target); + if (ret < 0) + return ret; + len = ret; + } else { + len = strlen(target_path); + } + + ret = fdt_setprop_placeholder(fdt, root_sym, name, + len + (len > 1) + rel_path_len + 1, &p); + if (ret < 0) + return ret; + + if (!target_path) { + /* again in case setprop_placeholder changed it */ + ret = overlay_get_target(fdt, fdto, fragment, &target_path); + if (ret < 0) + return ret; + target = ret; + } + + buf = p; + if (len > 1) { /* target is not root */ + if (!target_path) { + ret = fdt_get_path(fdt, target, buf, len + 1); + if (ret < 0) + return ret; + } else + memcpy(buf, target_path, len + 1); + + } else + len--; + + buf[len] = '/'; + memcpy(buf + len + 1, rel_path, rel_path_len); + buf[len + 1 + rel_path_len] = '\0'; + } + + return 0; +} + +int fdt_overlay_apply(void *fdt, void *fdto) +{ + uint32_t delta = fdt_get_max_phandle(fdt); + int ret; + + FDT_CHECK_HEADER(fdt); + FDT_CHECK_HEADER(fdto); + + ret = overlay_adjust_local_phandles(fdto, delta); + if (ret) + goto err; + + ret = overlay_update_local_references(fdto, delta); + if (ret) + goto err; + + ret = overlay_fixup_phandles(fdt, fdto); + if (ret) + goto err; + + ret = overlay_merge(fdt, fdto); + if (ret) + goto err; + + ret = overlay_symbol_update(fdt, fdto); + if (ret) + goto err; + + /* + * The overlay has been damaged, erase its magic. + */ + fdt_set_magic(fdto, ~0); + + return 0; + +err: + /* + * The overlay might have been damaged, erase its magic. + */ + fdt_set_magic(fdto, ~0); + + /* + * The base device tree might have been damaged, erase its + * magic. + */ + fdt_set_magic(fdt, ~0); + + return ret; +} diff --git a/scripts/dtc/libfdt/fdt_ro.c b/scripts/dtc/libfdt/fdt_ro.c index 3d00d2eee0e3..08de2cce674d 100644 --- a/scripts/dtc/libfdt/fdt_ro.c +++ b/scripts/dtc/libfdt/fdt_ro.c @@ -60,7 +60,7 @@ static int _fdt_nodename_eq(const void *fdt, int offset, { const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1); - if (! p) + if (!p) /* short match */ return 0; @@ -327,7 +327,7 @@ const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, const struct fdt_property *prop; prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp); - if (! prop) + if (!prop) return NULL; return prop->data; diff --git a/scripts/dtc/libfdt/fdt_rw.c b/scripts/dtc/libfdt/fdt_rw.c index 3fd5847377c9..5c3a2bb0bc6b 100644 --- a/scripts/dtc/libfdt/fdt_rw.c +++ b/scripts/dtc/libfdt/fdt_rw.c @@ -207,7 +207,7 @@ static int _fdt_resize_property(void *fdt, int nodeoffset, const char *name, int err; *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); - if (! (*prop)) + if (!*prop) return oldlen; if ((err = _fdt_splice_struct(fdt, (*prop)->data, FDT_TAGALIGN(oldlen), @@ -269,8 +269,8 @@ int fdt_set_name(void *fdt, int nodeoffset, const char *name) return 0; } -int fdt_setprop(void *fdt, int nodeoffset, const char *name, - const void *val, int len) +int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, + int len, void **prop_data) { struct fdt_property *prop; int err; @@ -283,8 +283,22 @@ int fdt_setprop(void *fdt, int nodeoffset, const char *name, if (err) return err; + *prop_data = prop->data; + return 0; +} + +int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + void *prop_data; + int err; + + err = fdt_setprop_placeholder(fdt, nodeoffset, name, len, &prop_data); + if (err) + return err; + if (len) - memcpy(prop->data, val, len); + memcpy(prop_data, val, len); return 0; } @@ -323,7 +337,7 @@ int fdt_delprop(void *fdt, int nodeoffset, const char *name) FDT_RW_CHECK_HEADER(fdt); prop = fdt_get_property_w(fdt, nodeoffset, name, &len); - if (! prop) + if (!prop) return len; proplen = sizeof(*prop) + FDT_TAGALIGN(len); diff --git a/scripts/dtc/libfdt/fdt_sw.c b/scripts/dtc/libfdt/fdt_sw.c index 6a804859fd0c..2bd15e7aef87 100644 --- a/scripts/dtc/libfdt/fdt_sw.c +++ b/scripts/dtc/libfdt/fdt_sw.c @@ -220,7 +220,7 @@ static int _fdt_find_add_string(void *fdt, const char *s) return offset; } -int fdt_property(void *fdt, const char *name, const void *val, int len) +int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp) { struct fdt_property *prop; int nameoff; @@ -238,7 +238,19 @@ int fdt_property(void *fdt, const char *name, const void *val, int len) prop->tag = cpu_to_fdt32(FDT_PROP); prop->nameoff = cpu_to_fdt32(nameoff); prop->len = cpu_to_fdt32(len); - memcpy(prop->data, val, len); + *valp = prop->data; + return 0; +} + +int fdt_property(void *fdt, const char *name, const void *val, int len) +{ + void *ptr; + int ret; + + ret = fdt_property_placeholder(fdt, name, len, &ptr); + if (ret) + return ret; + memcpy(ptr, val, len); return 0; } diff --git a/scripts/dtc/libfdt/fdt_wip.c b/scripts/dtc/libfdt/fdt_wip.c index 6aaab399929c..5e859198622b 100644 --- a/scripts/dtc/libfdt/fdt_wip.c +++ b/scripts/dtc/libfdt/fdt_wip.c @@ -82,7 +82,7 @@ int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, int proplen; propval = fdt_getprop(fdt, nodeoffset, name, &proplen); - if (! propval) + if (!propval) return proplen; if (proplen != len) @@ -107,7 +107,7 @@ int fdt_nop_property(void *fdt, int nodeoffset, const char *name) int len; prop = fdt_get_property_w(fdt, nodeoffset, name, &len); - if (! prop) + if (!prop) return len; _fdt_nop_region(prop, len + sizeof(*prop)); diff --git a/scripts/dtc/libfdt/libfdt.h b/scripts/dtc/libfdt/libfdt.h index ba86caa73d01..7f83023ee109 100644 --- a/scripts/dtc/libfdt/libfdt.h +++ b/scripts/dtc/libfdt/libfdt.h @@ -1314,6 +1314,22 @@ static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val) { return fdt_property_u32(fdt, name, val); } + +/** + * fdt_property_placeholder - add a new property and return a ptr to its value + * + * @fdt: pointer to the device tree blob + * @name: name of property to add + * @len: length of property value in bytes + * @valp: returns a pointer to where where the value should be placed + * + * returns: + * 0, on success + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_NOSPACE, standard meanings + */ +int fdt_property_placeholder(void *fdt, const char *name, int len, void **valp); + #define fdt_property_string(fdt, name, str) \ fdt_property(fdt, name, str, strlen(str)+1) int fdt_end_node(void *fdt); @@ -1432,6 +1448,37 @@ int fdt_set_name(void *fdt, int nodeoffset, const char *name); int fdt_setprop(void *fdt, int nodeoffset, const char *name, const void *val, int len); +/** + * fdt_setprop _placeholder - allocate space for a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @len: length of the property value + * @prop_data: return pointer to property data + * + * fdt_setprop_placeholer() allocates the named property in the given node. + * If the property exists it is resized. In either case a pointer to the + * property data is returned. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_setprop_placeholder(void *fdt, int nodeoffset, const char *name, + int len, void **prop_data); + /** * fdt_setprop_u32 - set a property to a 32-bit integer * @fdt: pointer to the device tree blob diff --git a/scripts/dtc/livetree.c b/scripts/dtc/livetree.c index 3673de07e4e5..6846ad2fd6d2 100644 --- a/scripts/dtc/livetree.c +++ b/scripts/dtc/livetree.c @@ -216,6 +216,28 @@ struct node *merge_nodes(struct node *old_node, struct node *new_node) return old_node; } +void add_orphan_node(struct node *dt, struct node *new_node, char *ref) +{ + static unsigned int next_orphan_fragment = 0; + struct node *node; + struct property *p; + struct data d = empty_data; + char *name; + + d = data_add_marker(d, REF_PHANDLE, ref); + d = data_append_integer(d, 0xffffffff, 32); + + p = build_property("target", d); + + xasprintf(&name, "fragment@%u", + next_orphan_fragment++); + name_node(new_node, "__overlay__"); + node = build_node(p, new_node); + name_node(node, name); + + add_child(dt, node); +} + struct node *chain_node(struct node *first, struct node *list) { assert(first->next_sibling == NULL); @@ -396,6 +418,12 @@ cell_t propval_cell(struct property *prop) return fdt32_to_cpu(*((fdt32_t *)prop->val.val)); } +cell_t propval_cell_n(struct property *prop, int n) +{ + assert(prop->val.len / sizeof(cell_t) >= n); + return fdt32_to_cpu(*((fdt32_t *)prop->val.val + n)); +} + struct property *get_property_by_label(struct node *tree, const char *label, struct node **node) { @@ -478,7 +506,8 @@ struct node *get_node_by_path(struct node *tree, const char *path) p = strchr(path, '/'); for_each_child(tree, child) { - if (p && strneq(path, child->name, p-path)) + if (p && (strlen(child->name) == p-path) && + strneq(path, child->name, p-path)) return get_node_by_path(child, p+1); else if (!p && streq(path, child->name)) return child; diff --git a/scripts/dtc/version_gen.h b/scripts/dtc/version_gen.h index 1229e07b4912..d88393cab14a 100644 --- a/scripts/dtc/version_gen.h +++ b/scripts/dtc/version_gen.h @@ -1 +1 @@ -#define DTC_VERSION "DTC 1.4.4-g756ffc4f" +#define DTC_VERSION "DTC 1.4.5-gb1a60033" -- cgit v1.3-6-gb490 From 6736ce27ce34d791ff2f030df6162527bd383a3c Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 30 Sep 2017 10:10:03 +0900 Subject: kbuild: rpm-pkg: remove ppc64 specific image handling This conditional was added by commit 1a0f3d422bb9 ("kbuild: fix make rpm for powerpc"). Its git-log explains the default kernel image is zImage, but obviously the current arch/powerpc/Makefile does not set KBUILD_IMAGE, so the image file is actually vmlinux. Moreover, since commit 09549aa1baa9 ("deb-pkg: Remove the KBUILD_IMAGE workaround"), all architectures are supposed to set the full path to the image in KBUILD_IMAGE. I see no good reason to differentiate ppc64 from others. Rip off the conditional. Signed-off-by: Masahiro Yamada --- scripts/package/mkspec | 5 ----- 1 file changed, 5 deletions(-) (limited to 'scripts') diff --git a/scripts/package/mkspec b/scripts/package/mkspec index f47f17aae135..ef007501effe 100755 --- a/scripts/package/mkspec +++ b/scripts/package/mkspec @@ -92,13 +92,8 @@ echo "%ifarch ia64" echo 'cp $KBUILD_IMAGE $RPM_BUILD_ROOT'"/boot/efi/vmlinuz-$KERNELRELEASE" echo 'ln -s '"efi/vmlinuz-$KERNELRELEASE" '$RPM_BUILD_ROOT'"/boot/" echo "%else" -echo "%ifarch ppc64" -echo "cp vmlinux arch/powerpc/boot" -echo "cp arch/powerpc/boot/"'$KBUILD_IMAGE $RPM_BUILD_ROOT'"/boot/vmlinuz-$KERNELRELEASE" -echo "%else" echo 'cp $KBUILD_IMAGE $RPM_BUILD_ROOT'"/boot/vmlinuz-$KERNELRELEASE" echo "%endif" -echo "%endif" echo 'make %{?_smp_mflags} INSTALL_HDR_PATH=$RPM_BUILD_ROOT/usr KBUILD_SRC= headers_install' echo 'cp System.map $RPM_BUILD_ROOT'"/boot/System.map-$KERNELRELEASE" -- cgit v1.3-6-gb490 From 81771ce2d1a9f9ce8739aac7af64bb03ff4b6a1a Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 30 Sep 2017 10:10:04 +0900 Subject: kbuild: rpm-pkg: install vmlinux.bz2 unconditionally This conditional was added by commit fc370ecfdb37 ("kbuild: add vmlinux to kernel rpm"). Its git-log mentioned vmlinux.bz2 was necessary for debugging, but did not explain why ppc64 was an exception. I see no problem to copy vmlinux.bz2 all the time. Signed-off-by: Masahiro Yamada --- scripts/package/mkspec | 3 --- 1 file changed, 3 deletions(-) (limited to 'scripts') diff --git a/scripts/package/mkspec b/scripts/package/mkspec index ef007501effe..a026c089d773 100755 --- a/scripts/package/mkspec +++ b/scripts/package/mkspec @@ -99,11 +99,8 @@ echo 'make %{?_smp_mflags} INSTALL_HDR_PATH=$RPM_BUILD_ROOT/usr KBUILD_SRC= head echo 'cp System.map $RPM_BUILD_ROOT'"/boot/System.map-$KERNELRELEASE" echo 'cp .config $RPM_BUILD_ROOT'"/boot/config-$KERNELRELEASE" - -echo "%ifnarch ppc64" echo 'bzip2 -9 --keep vmlinux' echo 'mv vmlinux.bz2 $RPM_BUILD_ROOT'"/boot/vmlinux-$KERNELRELEASE.bz2" -echo "%endif" if ! $PREBUILT; then echo 'rm -f $RPM_BUILD_ROOT'"/lib/modules/$KERNELRELEASE/build" -- cgit v1.3-6-gb490 From 5289c322ba994c7b07a4780243dca1feb610954f Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 30 Sep 2017 10:10:05 +0900 Subject: kbuild: rpm-pkg: clean up mkspec Clean up the mkspec without changing the behavior. - grep CONFIG_DRM=y more simply - move "EXCLUDE" out of the "%install" section because it can be computed when the spec file is generated - remove "BuildRoot:" field, which is now redundant - do not mkdir $RPM_BUILD_ROOT/lib/modules explicitly because it is automatically created by "make modules_install" - exclude "%package devel" from source package spec file because it does not make sense where "%files devel" is already excluded - exclude "%build" from source package spec file - remove unneeded "make clean" because we had already cleaned before making tar file - merge two %ifarch ia64 conditionals - replace KBUILD_IMAGE with direct use of $(make image_name) - remove trailing empty line from the spec file Signed-off-by: Masahiro Yamada --- scripts/package/mkspec | 44 ++++++++++++++++---------------------------- 1 file changed, 16 insertions(+), 28 deletions(-) (limited to 'scripts') diff --git a/scripts/package/mkspec b/scripts/package/mkspec index a026c089d773..97feb60e6482 100755 --- a/scripts/package/mkspec +++ b/scripts/package/mkspec @@ -10,19 +10,21 @@ # # how we were called determines which rpms we build and how we build them -if [ "$1" = "prebuilt" ]; then +if [ "$1" = prebuilt ]; then PREBUILT=true else PREBUILT=false fi -# starting to output the spec -if [ "`grep CONFIG_DRM=y .config | cut -f2 -d\=`" = "y" ]; then +if grep -q CONFIG_DRM=y .config; then PROVIDES=kernel-drm fi PROVIDES="$PROVIDES kernel-$KERNELRELEASE" -__KERNELRELEASE=`echo $KERNELRELEASE | sed -e "s/-/_/g"` +__KERNELRELEASE=$(echo $KERNELRELEASE | sed -e "s/-/_/g") +EXCLUDES="$RCS_TAR_IGNORE --exclude=.tmp_versions --exclude=*vmlinux* \ +--exclude=*.o --exclude=*.ko --exclude=*.cmd --exclude=Documentation \ +--exclude=.config.old --exclude=.missing-syscalls.d" echo "Name: kernel" echo "Summary: The Linux Kernel" @@ -37,7 +39,6 @@ if ! $PREBUILT; then echo "Source: kernel-$__KERNELRELEASE.tar.gz" fi -echo "BuildRoot: %{_tmppath}/%{name}-%{PACKAGE_VERSION}-root" echo "Provides: $PROVIDES" echo "%define __spec_install_post /usr/lib/rpm/brp-compress || :" echo "%define debug_package %{nil}" @@ -57,6 +58,8 @@ echo "header files define structures and constants that are needed for" echo "building most standard programs and are also needed for rebuilding the" echo "glibc package." echo "" + +if ! $PREBUILT; then echo "%package devel" echo "Summary: Development package for building kernel modules to match the $__KERNELRELEASE kernel" echo "Group: System Environment/Kernel" @@ -65,39 +68,26 @@ echo "%description -n kernel-devel" echo "This package provides kernel headers and makefiles sufficient to build modules" echo "against the $__KERNELRELEASE kernel package." echo "" - -if ! $PREBUILT; then echo "%prep" echo "%setup -q" echo "" -fi - echo "%build" - -if ! $PREBUILT; then -echo "make clean && make %{?_smp_mflags} KBUILD_BUILD_VERSION=%{release}" +echo "make %{?_smp_mflags} KBUILD_BUILD_VERSION=%{release}" echo "" fi echo "%install" -echo 'KBUILD_IMAGE=$(make image_name)' +echo 'mkdir -p $RPM_BUILD_ROOT/boot' echo "%ifarch ia64" -echo 'mkdir -p $RPM_BUILD_ROOT/boot/efi $RPM_BUILD_ROOT/lib/modules' -echo "%else" -echo 'mkdir -p $RPM_BUILD_ROOT/boot $RPM_BUILD_ROOT/lib/modules' -echo "%endif" - -echo 'INSTALL_MOD_PATH=$RPM_BUILD_ROOT make %{?_smp_mflags} KBUILD_SRC= modules_install' -echo "%ifarch ia64" -echo 'cp $KBUILD_IMAGE $RPM_BUILD_ROOT'"/boot/efi/vmlinuz-$KERNELRELEASE" +echo 'mkdir -p $RPM_BUILD_ROOT/boot/efi' +echo 'cp $(make image_name) $RPM_BUILD_ROOT'"/boot/efi/vmlinuz-$KERNELRELEASE" echo 'ln -s '"efi/vmlinuz-$KERNELRELEASE" '$RPM_BUILD_ROOT'"/boot/" echo "%else" -echo 'cp $KBUILD_IMAGE $RPM_BUILD_ROOT'"/boot/vmlinuz-$KERNELRELEASE" +echo 'cp $(make image_name) $RPM_BUILD_ROOT'"/boot/vmlinuz-$KERNELRELEASE" echo "%endif" - +echo 'make %{?_smp_mflags} INSTALL_MOD_PATH=$RPM_BUILD_ROOT KBUILD_SRC= modules_install' echo 'make %{?_smp_mflags} INSTALL_HDR_PATH=$RPM_BUILD_ROOT/usr KBUILD_SRC= headers_install' echo 'cp System.map $RPM_BUILD_ROOT'"/boot/System.map-$KERNELRELEASE" - echo 'cp .config $RPM_BUILD_ROOT'"/boot/config-$KERNELRELEASE" echo 'bzip2 -9 --keep vmlinux' echo 'mv vmlinux.bz2 $RPM_BUILD_ROOT'"/boot/vmlinux-$KERNELRELEASE.bz2" @@ -106,8 +96,7 @@ if ! $PREBUILT; then echo 'rm -f $RPM_BUILD_ROOT'"/lib/modules/$KERNELRELEASE/build" echo 'rm -f $RPM_BUILD_ROOT'"/lib/modules/$KERNELRELEASE/source" echo "mkdir -p "'$RPM_BUILD_ROOT'"/usr/src/kernels/$KERNELRELEASE" -echo "EXCLUDES=\"$RCS_TAR_IGNORE --exclude .tmp_versions --exclude=*vmlinux* --exclude=*.o --exclude=*.ko --exclude=*.cmd --exclude=Documentation --exclude .config.old --exclude .missing-syscalls.d\"" -echo "tar "'$EXCLUDES'" -cf- . | (cd "'$RPM_BUILD_ROOT'"/usr/src/kernels/$KERNELRELEASE;tar xvf -)" +echo "tar cf - . $EXCLUDES | tar xf - -C "'$RPM_BUILD_ROOT'"/usr/src/kernels/$KERNELRELEASE" echo 'cd $RPM_BUILD_ROOT'"/lib/modules/$KERNELRELEASE" echo "ln -sf /usr/src/kernels/$KERNELRELEASE build" echo "ln -sf /usr/src/kernels/$KERNELRELEASE source" @@ -146,12 +135,11 @@ echo "" echo "%files headers" echo '%defattr (-, root, root)' echo "/usr/include" -echo "" if ! $PREBUILT; then +echo "" echo "%files devel" echo '%defattr (-, root, root)' echo "/usr/src/kernels/$KERNELRELEASE" echo "/lib/modules/$KERNELRELEASE/build" echo "/lib/modules/$KERNELRELEASE/source" -echo "" fi -- cgit v1.3-6-gb490 From 278ae6040397f37fa6a96a6b86ed02d4762080a7 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 22 Sep 2017 14:31:13 +0900 Subject: kbuild: link-vmlinux.sh: simplify .version increment Since commit 1f2bfbd00e46 ("kbuild: link of vmlinux moved to a script"), it is easy to increment .version without using a temporary file .old_version. I do not see anybody who creates the .tmp_version. Probably it is a left-over of commit 4e25d8bb9550fb ("[PATCH] kbuild: adjust .version updating"). Just remove it. Signed-off-by: Masahiro Yamada --- Makefile | 2 +- scripts/link-vmlinux.sh | 15 +++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) (limited to 'scripts') diff --git a/Makefile b/Makefile index cf007a31d575..9aa47218100e 100644 --- a/Makefile +++ b/Makefile @@ -1278,7 +1278,7 @@ CLEAN_DIRS += $(MODVERDIR) # Directories & files removed with 'make mrproper' MRPROPER_DIRS += include/config usr/include include/generated \ arch/*/include/generated .tmp_objdiff -MRPROPER_FILES += .config .config.old .version .old_version \ +MRPROPER_FILES += .config .config.old .version \ Module.symvers tags TAGS cscope* GPATH GTAGS GRTAGS GSYMS \ signing_key.pem signing_key.priv signing_key.x509 \ x509.genkey extra_certificates signing_key.x509.keyid \ diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh index e7b7eee31538..0cdb25b66e6f 100755 --- a/scripts/link-vmlinux.sh +++ b/scripts/link-vmlinux.sh @@ -187,10 +187,8 @@ sortextable() # Delete output files in case of error cleanup() { - rm -f .old_version rm -f .tmp_System.map rm -f .tmp_kallsyms* - rm -f .tmp_version rm -f .tmp_vmlinux* rm -f built-in.o rm -f System.map @@ -238,12 +236,12 @@ esac # Update version info GEN .version -if [ ! -r .version ]; then - rm -f .version; - echo 1 >.version; +if [ -r .version ]; then + VERSION=$(expr 0$(cat .version) + 1) + echo $VERSION > .version else - mv .version .old_version; - expr 0$(cat .old_version) + 1 >.version; + rm -f .version + echo 1 > .version fi; # final build of init/ @@ -331,6 +329,3 @@ if [ -n "${CONFIG_KALLSYMS}" ]; then exit 1 fi fi - -# We made a new kernel - delete old version file -rm -f .old_version -- cgit v1.3-6-gb490 From 37131ec4f9cb60750be6f75fca20ff6bbf1b8efd Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 22 Sep 2017 14:31:14 +0900 Subject: kbuild: mkcompile_h: do not create .version This script does not need to create .version; it will be created by scripts/link-vmlinux.sh later. Clean-up the code slightly. Signed-off-by: Masahiro Yamada --- scripts/mkcompile_h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'scripts') diff --git a/scripts/mkcompile_h b/scripts/mkcompile_h index fd8fdb91581d..f1ee4ebd6518 100755 --- a/scripts/mkcompile_h +++ b/scripts/mkcompile_h @@ -27,12 +27,7 @@ LC_ALL=C export LC_ALL if [ -z "$KBUILD_BUILD_VERSION" ]; then - if [ -r .version ]; then - VERSION=`cat .version` - else - VERSION=0 - echo 0 > .version - fi + VERSION=$(cat .version 2>/dev/null || echo 1) else VERSION=$KBUILD_BUILD_VERSION fi -- cgit v1.3-6-gb490 From 9d022c540606a5a8ae5d1cc02fc12de362ba4585 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 4 Oct 2017 12:56:04 +0900 Subject: kbuild: replace $(hdr-arch) with $(SRCARCH) Since commit 5e53879008b9 ("sparc,sparc64: unify Makefile"), hdr-arch and SRCARCH always match. Signed-off-by: Masahiro Yamada Reviewed-by: Douglas Anderson --- Makefile | 21 +++++++++------------ scripts/Makefile.headersinst | 2 +- 2 files changed, 10 insertions(+), 13 deletions(-) (limited to 'scripts') diff --git a/Makefile b/Makefile index 9aa47218100e..27fcdc1d4ec9 100644 --- a/Makefile +++ b/Makefile @@ -283,9 +283,6 @@ ifeq ($(ARCH),tilegx) SRCARCH := tile endif -# Where to locate arch specific headers -hdr-arch := $(SRCARCH) - KCONFIG_CONFIG ?= .config export KCONFIG_CONFIG @@ -378,8 +375,8 @@ CFLAGS_KCOV := $(call cc-option,-fsanitize-coverage=trace-pc,) # Use USERINCLUDE when you must reference the UAPI directories only. USERINCLUDE := \ - -I$(srctree)/arch/$(hdr-arch)/include/uapi \ - -I$(objtree)/arch/$(hdr-arch)/include/generated/uapi \ + -I$(srctree)/arch/$(SRCARCH)/include/uapi \ + -I$(objtree)/arch/$(SRCARCH)/include/generated/uapi \ -I$(srctree)/include/uapi \ -I$(objtree)/include/generated/uapi \ -include $(srctree)/include/linux/kconfig.h @@ -387,8 +384,8 @@ USERINCLUDE := \ # Use LINUXINCLUDE when you must reference the include/ directory. # Needed to be compatible with the O= option LINUXINCLUDE := \ - -I$(srctree)/arch/$(hdr-arch)/include \ - -I$(objtree)/arch/$(hdr-arch)/include/generated \ + -I$(srctree)/arch/$(SRCARCH)/include \ + -I$(objtree)/arch/$(SRCARCH)/include/generated \ $(if $(KBUILD_SRC), -I$(srctree)/include) \ -I$(objtree)/include \ $(USERINCLUDE) @@ -1134,8 +1131,8 @@ headerdep: #Default location for installed headers export INSTALL_HDR_PATH = $(objtree)/usr -# If we do an all arch process set dst to include/arch-$(hdr-arch) -hdr-dst = $(if $(KBUILD_HEADERS), dst=include/arch-$(hdr-arch), dst=include) +# If we do an all arch process set dst to include/arch-$(SRCARCH) +hdr-dst = $(if $(KBUILD_HEADERS), dst=include/arch-$(SRCARCH), dst=include) PHONY += archheaders archheaders: @@ -1153,10 +1150,10 @@ headers_install_all: PHONY += headers_install headers_install: __headers - $(if $(wildcard $(srctree)/arch/$(hdr-arch)/include/uapi/asm/Kbuild),, \ + $(if $(wildcard $(srctree)/arch/$(SRCARCH)/include/uapi/asm/Kbuild),, \ $(error Headers not exportable for the $(SRCARCH) architecture)) $(Q)$(MAKE) $(hdr-inst)=include/uapi dst=include - $(Q)$(MAKE) $(hdr-inst)=arch/$(hdr-arch)/include/uapi $(hdr-dst) + $(Q)$(MAKE) $(hdr-inst)=arch/$(SRCARCH)/include/uapi $(hdr-dst) PHONY += headers_check_all headers_check_all: headers_install_all @@ -1165,7 +1162,7 @@ headers_check_all: headers_install_all PHONY += headers_check headers_check: headers_install $(Q)$(MAKE) $(hdr-inst)=include/uapi dst=include HDRCHECK=1 - $(Q)$(MAKE) $(hdr-inst)=arch/$(hdr-arch)/include/uapi $(hdr-dst) HDRCHECK=1 + $(Q)$(MAKE) $(hdr-inst)=arch/$(SRCARCH)/include/uapi $(hdr-dst) HDRCHECK=1 # --------------------------------------------------------------------------- # Kernel selftest diff --git a/scripts/Makefile.headersinst b/scripts/Makefile.headersinst index 343d586e566e..5692d7a66163 100644 --- a/scripts/Makefile.headersinst +++ b/scripts/Makefile.headersinst @@ -30,7 +30,7 @@ __headers: $(subdirs) $(subdirs): $(Q)$(MAKE) $(hdr-inst)=$(obj)/$@ dst=$(dst)/$@ -# Skip header install/check for include/uapi and arch/$(hdr-arch)/include/uapi. +# Skip header install/check for include/uapi and arch/$(SRCARCH)/include/uapi. # We have only sub-directories there. skip-inst := $(if $(filter %/uapi,$(obj)),1) -- cgit v1.3-6-gb490 From e45fe7f788dd1395befe5639149ad8dacfbd94ab Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 25 Oct 2017 10:59:13 -0500 Subject: scripts/dtc: Update to upstream version v1.4.5-6-gc1e55a5513e9 Pickup the fix for handling unresolved phandles in overlays. This adds the following commits from upstream: c1e55a5513e9 checks: fix handling of unresolved phandles for dts plugins f8872e29ce06 tests: Avoid 64-bit arithmetic in assembler 48c91c08bcfa libfdt: add stringlist functions to linker script Signed-off-by: Rob Herring --- scripts/dtc/checks.c | 9 +++++++++ scripts/dtc/version_gen.h | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/dtc/checks.c b/scripts/dtc/checks.c index 08a3a29edae3..e66138449886 100644 --- a/scripts/dtc/checks.c +++ b/scripts/dtc/checks.c @@ -988,6 +988,10 @@ static void check_property_phandle_args(struct check *c, * entries when each index position has a specific definition. */ if (phandle == 0 || phandle == -1) { + /* Give up if this is an overlay with external references */ + if (dti->dtsflags & DTSF_PLUGIN) + break; + cellsize = 0; continue; } @@ -1176,6 +1180,11 @@ static void check_interrupts_property(struct check *c, prop = get_property(parent, "interrupt-parent"); if (prop) { phandle = propval_cell(prop); + /* Give up if this is an overlay with external references */ + if ((phandle == 0 || phandle == -1) && + (dti->dtsflags & DTSF_PLUGIN)) + return; + irq_node = get_node_by_phandle(root, phandle); if (!irq_node) { FAIL(c, dti, "Bad interrupt-parent phandle for %s", diff --git a/scripts/dtc/version_gen.h b/scripts/dtc/version_gen.h index d88393cab14a..6a4e84798966 100644 --- a/scripts/dtc/version_gen.h +++ b/scripts/dtc/version_gen.h @@ -1 +1 @@ -#define DTC_VERSION "DTC 1.4.5-gb1a60033" +#define DTC_VERSION "DTC 1.4.5-gc1e55a55" -- cgit v1.3-6-gb490 From 8fdc3fbbd5b646650efa66d577b88807a948a1d1 Mon Sep 17 00:00:00 2001 From: Cao jin Date: Mon, 9 Oct 2017 11:49:11 +0800 Subject: kbuild: comments cleanup in Makefile.lib It has: 1. Move comments close to what it want to comment. 2. Comments cleanup & improvement. Signed-off-by: Cao jin Signed-off-by: Masahiro Yamada --- scripts/Makefile.lib | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) (limited to 'scripts') diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 5e975fee0f5b..580e605118e4 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -4,8 +4,7 @@ ccflags-y += $(EXTRA_CFLAGS) cppflags-y += $(EXTRA_CPPFLAGS) ldflags-y += $(EXTRA_LDFLAGS) -# -# flags that take effect in sub directories +# flags that take effect in current and sub directories export KBUILD_SUBDIR_ASFLAGS := $(KBUILD_SUBDIR_ASFLAGS) $(subdir-asflags-y) export KBUILD_SUBDIR_CCFLAGS := $(KBUILD_SUBDIR_CCFLAGS) $(subdir-ccflags-y) @@ -14,14 +13,16 @@ export KBUILD_SUBDIR_CCFLAGS := $(KBUILD_SUBDIR_CCFLAGS) $(subdir-ccflags-y) # When an object is listed to be built compiled-in and modular, # only build the compiled-in version - obj-m := $(filter-out $(obj-y),$(obj-m)) # Libraries are always collected in one lib file. # Filter out objects already built-in - lib-y := $(filter-out $(obj-y), $(sort $(lib-y) $(lib-m))) +# Determine modorder. +# Unfortunately, we don't have information about ordering between -y +# and -m subdirs. Just put -y's first. +modorder := $(patsubst %/,%/modules.order, $(filter %/, $(obj-y)) $(obj-m:.o=.ko)) # Handle objects in subdirs # --------------------------------------------------------------------------- @@ -29,12 +30,6 @@ lib-y := $(filter-out $(obj-y), $(sort $(lib-y) $(lib-m))) # and add the directory to the list of dirs to descend into: $(subdir-y) # o if we encounter foo/ in $(obj-m), remove it from $(obj-m) # and add the directory to the list of dirs to descend into: $(subdir-m) - -# Determine modorder. -# Unfortunately, we don't have information about ordering between -y -# and -m subdirs. Just put -y's first. -modorder := $(patsubst %/,%/modules.order, $(filter %/, $(obj-y)) $(obj-m:.o=.ko)) - __subdir-y := $(patsubst %/,%,$(filter %/, $(obj-y))) subdir-y += $(__subdir-y) __subdir-m := $(patsubst %/,%,$(filter %/, $(obj-m))) @@ -43,10 +38,9 @@ obj-y := $(patsubst %/, %/built-in.o, $(obj-y)) obj-m := $(filter-out %/, $(obj-m)) # Subdirectories we need to descend into - subdir-ym := $(sort $(subdir-y) $(subdir-m)) -# if $(foo-objs) exists, foo.o is a composite object +# if $(foo-objs), $(foo-y), or $(foo-m) exists, foo.o is a composite object multi-used-y := $(sort $(foreach m,$(obj-y), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))), $(m)))) multi-used-m := $(sort $(foreach m,$(obj-m), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y)) $($(m:.o=-m))), $(m)))) multi-used := $(multi-used-y) $(multi-used-m) @@ -90,8 +84,7 @@ subdir-ym := $(addprefix $(obj)/,$(subdir-ym)) obj-dirs := $(addprefix $(obj)/,$(obj-dirs)) # These flags are needed for modversions and compiling, so we define them here -# already -# $(modname_flags) #defines KBUILD_MODNAME as the name of the module it will +# $(modname_flags) defines KBUILD_MODNAME as the name of the module it will # end up in (or would, if it gets compiled in) # Note: Files that end up in two or more modules are compiled without the # KBUILD_MODNAME definition. The reason is that any made-up name would -- cgit v1.3-6-gb490 From 4e13d47c5806bafb5e524b08a9d759b606b1851c Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 10 Oct 2017 20:43:21 +0900 Subject: kbuild: remove KBUILD_SUBDIR_ASFLAGS and KBUILD_SUBDIR_CCFLAGS Accumulate subdir-{cc,as}flags-y directly to KBUILD_{A,C}FLAGS. Remove KBUILD_SUBDIR_{AS,CC}FLAGS. Signed-off-by: Masahiro Yamada Reviewed-by: Cao jin --- scripts/Makefile.lib | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 580e605118e4..4d88ad70fd96 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -5,8 +5,8 @@ cppflags-y += $(EXTRA_CPPFLAGS) ldflags-y += $(EXTRA_LDFLAGS) # flags that take effect in current and sub directories -export KBUILD_SUBDIR_ASFLAGS := $(KBUILD_SUBDIR_ASFLAGS) $(subdir-asflags-y) -export KBUILD_SUBDIR_CCFLAGS := $(KBUILD_SUBDIR_CCFLAGS) $(subdir-ccflags-y) +KBUILD_AFLAGS += $(subdir-asflags-y) +KBUILD_CFLAGS += $(subdir-ccflags-y) # Figure out what we need to build from the various variables # =========================================================================== @@ -94,10 +94,10 @@ basename_flags = -DKBUILD_BASENAME=$(call name-fix,$(basetarget)) modname_flags = $(if $(filter 1,$(words $(modname))),\ -DKBUILD_MODNAME=$(call name-fix,$(modname))) -orig_c_flags = $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(KBUILD_SUBDIR_CCFLAGS) \ +orig_c_flags = $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) \ $(ccflags-y) $(CFLAGS_$(basetarget).o) _c_flags = $(filter-out $(CFLAGS_REMOVE_$(basetarget).o), $(orig_c_flags)) -orig_a_flags = $(KBUILD_CPPFLAGS) $(KBUILD_AFLAGS) $(KBUILD_SUBDIR_ASFLAGS) \ +orig_a_flags = $(KBUILD_CPPFLAGS) $(KBUILD_AFLAGS) \ $(asflags-y) $(AFLAGS_$(basetarget).o) _a_flags = $(filter-out $(AFLAGS_REMOVE_$(basetarget).o), $(orig_a_flags)) _cpp_flags = $(KBUILD_CPPFLAGS) $(cppflags-y) $(CPPFLAGS_$(@F)) -- cgit v1.3-6-gb490 From 7e7962dd1a5307bca1793a3f9a98ad5514306c7a Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 5 Nov 2017 14:30:53 +0900 Subject: kbuild: handle dtb-y and CONFIG_OF_ALL_DTBS natively in Makefile.lib If CONFIG_OF_ALL_DTBS is enabled, "make ARCH=arm64 dtbs" compiles each DTB twice; one from arch/arm64/boot/dts/*/Makefile and the other from the dtb-$(CONFIG_OF_ALL_DTBS) line in arch/arm64/boot/dts/Makefile. It could be a race problem when building DTBS in parallel. Another minor issue is CONFIG_OF_ALL_DTBS covers only *.dts in vendor sub-directories, so this broke when Broadcom added one more hierarchy in arch/arm64/boot/dts/broadcom//. One idea to fix the issues in a clean way is to move DTB handling to Kbuild core scripts. Makefile.dtbinst already recognizes dtb-y natively, so it should not hurt to do so. Add $(dtb-y) to extra-y, and $(dtb-) as well if CONFIG_OF_ALL_DTBS is enabled. All clutter things in Makefiles go away. As a bonus clean-up, I also removed dts-dirs. Just use subdir-y directly to traverse sub-directories. Signed-off-by: Masahiro Yamada Acked-by: Arnd Bergmann [robh: corrected BUILTIN_DTB to CONFIG_BUILTIN_DTB] Signed-off-by: Rob Herring --- arch/arc/boot/dts/Makefile | 7 ++- arch/arm/boot/dts/Makefile | 5 -- arch/arm64/boot/dts/Makefile | 58 ++++++++++-------------- arch/arm64/boot/dts/actions/Makefile | 3 -- arch/arm64/boot/dts/al/Makefile | 3 -- arch/arm64/boot/dts/allwinner/Makefile | 3 -- arch/arm64/boot/dts/altera/Makefile | 3 -- arch/arm64/boot/dts/amd/Makefile | 3 -- arch/arm64/boot/dts/amlogic/Makefile | 3 -- arch/arm64/boot/dts/apm/Makefile | 3 -- arch/arm64/boot/dts/arm/Makefile | 3 -- arch/arm64/boot/dts/broadcom/Makefile | 6 +-- arch/arm64/boot/dts/broadcom/northstar2/Makefile | 3 -- arch/arm64/boot/dts/broadcom/stingray/Makefile | 3 -- arch/arm64/boot/dts/cavium/Makefile | 3 -- arch/arm64/boot/dts/exynos/Makefile | 3 -- arch/arm64/boot/dts/freescale/Makefile | 3 -- arch/arm64/boot/dts/hisilicon/Makefile | 3 -- arch/arm64/boot/dts/lg/Makefile | 3 -- arch/arm64/boot/dts/marvell/Makefile | 3 -- arch/arm64/boot/dts/mediatek/Makefile | 3 -- arch/arm64/boot/dts/nvidia/Makefile | 2 - arch/arm64/boot/dts/qcom/Makefile | 3 -- arch/arm64/boot/dts/realtek/Makefile | 3 -- arch/arm64/boot/dts/renesas/Makefile | 2 - arch/arm64/boot/dts/rockchip/Makefile | 3 -- arch/arm64/boot/dts/socionext/Makefile | 2 - arch/arm64/boot/dts/sprd/Makefile | 3 -- arch/arm64/boot/dts/xilinx/Makefile | 3 -- arch/arm64/boot/dts/zte/Makefile | 3 -- arch/h8300/boot/dts/Makefile | 5 -- arch/metag/boot/dts/Makefile | 5 -- arch/mips/boot/dts/Makefile | 32 ++++++------- arch/mips/boot/dts/brcm/Makefile | 2 - arch/mips/boot/dts/cavium-octeon/Makefile | 2 - arch/mips/boot/dts/img/Makefile | 2 - arch/mips/boot/dts/ingenic/Makefile | 2 - arch/mips/boot/dts/lantiq/Makefile | 2 - arch/mips/boot/dts/mti/Makefile | 2 - arch/mips/boot/dts/netlogic/Makefile | 2 - arch/mips/boot/dts/ni/Makefile | 2 - arch/mips/boot/dts/pic32/Makefile | 2 - arch/mips/boot/dts/qca/Makefile | 2 - arch/mips/boot/dts/ralink/Makefile | 2 - arch/mips/boot/dts/xilfpga/Makefile | 2 - arch/xtensa/boot/dts/Makefile | 7 ++- scripts/Makefile.dtbinst | 6 +-- scripts/Makefile.lib | 5 ++ 48 files changed, 53 insertions(+), 182 deletions(-) (limited to 'scripts') diff --git a/arch/arc/boot/dts/Makefile b/arch/arc/boot/dts/Makefile index 1257db1dc1be..9ece28b0a83f 100644 --- a/arch/arc/boot/dts/Makefile +++ b/arch/arc/boot/dts/Makefile @@ -10,7 +10,6 @@ dtb-y := $(builtindtb-y).dtb .SECONDARY: $(obj)/$(builtindtb-y).dtb.S -dtstree := $(srctree)/$(src) -dtb-$(CONFIG_OF_ALL_DTBS) := $(patsubst $(dtstree)/%.dts,%.dtb, $(wildcard $(dtstree)/*.dts)) - -always := $(dtb-y) +# for CONFIG_OF_ALL_DTBS test +dtstree := $(srctree)/$(src) +dtb- := $(patsubst $(dtstree)/%.dts,%.dtb, $(wildcard $(dtstree)/*.dts)) diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 5eeefbcfe98c..4b650d6de017 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -1069,8 +1069,3 @@ dtb-$(CONFIG_ARCH_ASPEED) += aspeed-bmc-opp-palmetto.dtb \ aspeed-bmc-opp-romulus.dtb \ aspeed-ast2500-evb.dtb endif - -dtstree := $(srctree)/$(src) -dtb-$(CONFIG_OF_ALL_DTBS) := $(patsubst $(dtstree)/%.dts,%.dtb, $(wildcard $(dtstree)/*.dts)) - -always := $(dtb-y) diff --git a/arch/arm64/boot/dts/Makefile b/arch/arm64/boot/dts/Makefile index 8e1951273fd7..a7ecb42e84e6 100644 --- a/arch/arm64/boot/dts/Makefile +++ b/arch/arm64/boot/dts/Makefile @@ -1,33 +1,25 @@ -dts-dirs += actions -dts-dirs += al -dts-dirs += allwinner -dts-dirs += altera -dts-dirs += amd -dts-dirs += amlogic -dts-dirs += apm -dts-dirs += arm -dts-dirs += broadcom -dts-dirs += cavium -dts-dirs += exynos -dts-dirs += freescale -dts-dirs += hisilicon -dts-dirs += marvell -dts-dirs += mediatek -dts-dirs += nvidia -dts-dirs += qcom -dts-dirs += realtek -dts-dirs += renesas -dts-dirs += rockchip -dts-dirs += socionext -dts-dirs += sprd -dts-dirs += xilinx -dts-dirs += lg -dts-dirs += zte - -subdir-y := $(dts-dirs) - -dtstree := $(srctree)/$(src) - -dtb-$(CONFIG_OF_ALL_DTBS) := $(patsubst $(dtstree)/%.dts,%.dtb, $(foreach d,$(dts-dirs), $(wildcard $(dtstree)/$(d)/*.dts))) - -always := $(dtb-y) +subdir-y += actions +subdir-y += al +subdir-y += allwinner +subdir-y += altera +subdir-y += amd +subdir-y += amlogic +subdir-y += apm +subdir-y += arm +subdir-y += broadcom +subdir-y += cavium +subdir-y += exynos +subdir-y += freescale +subdir-y += hisilicon +subdir-y += marvell +subdir-y += mediatek +subdir-y += nvidia +subdir-y += qcom +subdir-y += realtek +subdir-y += renesas +subdir-y += rockchip +subdir-y += socionext +subdir-y += sprd +subdir-y += xilinx +subdir-y += lg +subdir-y += zte diff --git a/arch/arm64/boot/dts/actions/Makefile b/arch/arm64/boot/dts/actions/Makefile index 89bb1b534492..cc4661256356 100644 --- a/arch/arm64/boot/dts/actions/Makefile +++ b/arch/arm64/boot/dts/actions/Makefile @@ -1,4 +1 @@ dtb-$(CONFIG_ARCH_ACTIONS) += s900-bubblegum-96.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/al/Makefile b/arch/arm64/boot/dts/al/Makefile index 8606a57e567f..036e387112ed 100644 --- a/arch/arm64/boot/dts/al/Makefile +++ b/arch/arm64/boot/dts/al/Makefile @@ -1,4 +1 @@ dtb-$(CONFIG_ARCH_ALPINE) += alpine-v2-evp.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/allwinner/Makefile b/arch/arm64/boot/dts/allwinner/Makefile index 871ed768f85d..4dab590d24a5 100644 --- a/arch/arm64/boot/dts/allwinner/Makefile +++ b/arch/arm64/boot/dts/allwinner/Makefile @@ -8,6 +8,3 @@ dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h5-orangepi-pc2.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h5-orangepi-prime.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h5-orangepi-zero-plus2.dtb dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h5-nanopi-neo2.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/altera/Makefile b/arch/arm64/boot/dts/altera/Makefile index 7511b51d9b4a..68ba0882a8bb 100644 --- a/arch/arm64/boot/dts/altera/Makefile +++ b/arch/arm64/boot/dts/altera/Makefile @@ -1,4 +1 @@ dtb-$(CONFIG_ARCH_STRATIX10) += socfpga_stratix10_socdk.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/amd/Makefile b/arch/arm64/boot/dts/amd/Makefile index cb1c77967c9e..465cea694a6a 100644 --- a/arch/arm64/boot/dts/amd/Makefile +++ b/arch/arm64/boot/dts/amd/Makefile @@ -1,6 +1,3 @@ dtb-$(CONFIG_ARCH_SEATTLE) += amd-overdrive.dtb \ amd-overdrive-rev-b0.dtb amd-overdrive-rev-b1.dtb \ husky.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/amlogic/Makefile b/arch/arm64/boot/dts/amlogic/Makefile index d86440346962..4eb8f829472e 100644 --- a/arch/arm64/boot/dts/amlogic/Makefile +++ b/arch/arm64/boot/dts/amlogic/Makefile @@ -19,6 +19,3 @@ dtb-$(CONFIG_ARCH_MESON) += meson-gxm-nexbox-a1.dtb dtb-$(CONFIG_ARCH_MESON) += meson-gxm-q200.dtb dtb-$(CONFIG_ARCH_MESON) += meson-gxm-q201.dtb dtb-$(CONFIG_ARCH_MESON) += meson-gxm-rbox-pro.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/apm/Makefile b/arch/arm64/boot/dts/apm/Makefile index 4334978f522e..b96cd388ca7b 100644 --- a/arch/arm64/boot/dts/apm/Makefile +++ b/arch/arm64/boot/dts/apm/Makefile @@ -1,5 +1,2 @@ dtb-$(CONFIG_ARCH_XGENE) += apm-mustang.dtb dtb-$(CONFIG_ARCH_XGENE) += apm-merlin.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/arm/Makefile b/arch/arm64/boot/dts/arm/Makefile index 01c342f61ed3..7ca6a6ec3d68 100644 --- a/arch/arm64/boot/dts/arm/Makefile +++ b/arch/arm64/boot/dts/arm/Makefile @@ -2,6 +2,3 @@ dtb-$(CONFIG_ARCH_VEXPRESS) += foundation-v8.dtb foundation-v8-gicv3.dtb dtb-$(CONFIG_ARCH_VEXPRESS) += juno.dtb juno-r1.dtb juno-r2.dtb dtb-$(CONFIG_ARCH_VEXPRESS) += rtsm_ve-aemv8a.dtb dtb-$(CONFIG_ARCH_VEXPRESS) += vexpress-v2f-1xv7-ca53x2.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/broadcom/Makefile b/arch/arm64/boot/dts/broadcom/Makefile index d720d0dc7043..da268c69787f 100644 --- a/arch/arm64/boot/dts/broadcom/Makefile +++ b/arch/arm64/boot/dts/broadcom/Makefile @@ -1,6 +1,4 @@ dtb-$(CONFIG_ARCH_BCM2835) += bcm2837-rpi-3-b.dtb -dts-dirs += northstar2 -dts-dirs += stingray -always := $(dtb-y) -subdir-y := $(dts-dirs) +subdir-y += northstar2 +subdir-y += stingray diff --git a/arch/arm64/boot/dts/broadcom/northstar2/Makefile b/arch/arm64/boot/dts/broadcom/northstar2/Makefile index c589b9b55da8..83736004336d 100644 --- a/arch/arm64/boot/dts/broadcom/northstar2/Makefile +++ b/arch/arm64/boot/dts/broadcom/northstar2/Makefile @@ -1,5 +1,2 @@ dtb-$(CONFIG_ARCH_BCM_IPROC) += ns2-svk.dtb dtb-$(CONFIG_ARCH_BCM_IPROC) += ns2-xmc.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/broadcom/stingray/Makefile b/arch/arm64/boot/dts/broadcom/stingray/Makefile index 8edcc5264be8..ea5516b1f6da 100644 --- a/arch/arm64/boot/dts/broadcom/stingray/Makefile +++ b/arch/arm64/boot/dts/broadcom/stingray/Makefile @@ -1,5 +1,2 @@ dtb-$(CONFIG_ARCH_BCM_IPROC) += bcm958742k.dtb dtb-$(CONFIG_ARCH_BCM_IPROC) += bcm958742t.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/cavium/Makefile b/arch/arm64/boot/dts/cavium/Makefile index c63145e50271..f7c0ca8d3060 100644 --- a/arch/arm64/boot/dts/cavium/Makefile +++ b/arch/arm64/boot/dts/cavium/Makefile @@ -1,5 +1,2 @@ dtb-$(CONFIG_ARCH_THUNDER) += thunder-88xx.dtb dtb-$(CONFIG_ARCH_THUNDER2) += thunder2-99xx.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/exynos/Makefile b/arch/arm64/boot/dts/exynos/Makefile index 4633adf4cbdd..14031e159a16 100644 --- a/arch/arm64/boot/dts/exynos/Makefile +++ b/arch/arm64/boot/dts/exynos/Makefile @@ -2,6 +2,3 @@ dtb-$(CONFIG_ARCH_EXYNOS) += \ exynos5433-tm2.dtb \ exynos5433-tm2e.dtb \ exynos7-espresso.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/freescale/Makefile b/arch/arm64/boot/dts/freescale/Makefile index fe18e3d7b8e8..f1b53631363e 100644 --- a/arch/arm64/boot/dts/freescale/Makefile +++ b/arch/arm64/boot/dts/freescale/Makefile @@ -12,6 +12,3 @@ dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls2080a-rdb.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls2080a-simu.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls2088a-qds.dtb dtb-$(CONFIG_ARCH_LAYERSCAPE) += fsl-ls2088a-rdb.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/hisilicon/Makefile b/arch/arm64/boot/dts/hisilicon/Makefile index cb25d7a5cb5c..942c9ab760bf 100644 --- a/arch/arm64/boot/dts/hisilicon/Makefile +++ b/arch/arm64/boot/dts/hisilicon/Makefile @@ -4,6 +4,3 @@ dtb-$(CONFIG_ARCH_HISI) += hi6220-hikey.dtb dtb-$(CONFIG_ARCH_HISI) += hip05-d02.dtb dtb-$(CONFIG_ARCH_HISI) += hip06-d03.dtb dtb-$(CONFIG_ARCH_HISI) += hip07-d05.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/lg/Makefile b/arch/arm64/boot/dts/lg/Makefile index c0bbe06b95de..de5dc19b59ff 100644 --- a/arch/arm64/boot/dts/lg/Makefile +++ b/arch/arm64/boot/dts/lg/Makefile @@ -1,5 +1,2 @@ dtb-$(CONFIG_ARCH_LG1K) += lg1312-ref.dtb dtb-$(CONFIG_ARCH_LG1K) += lg1313-ref.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/marvell/Makefile b/arch/arm64/boot/dts/marvell/Makefile index b471235d9e66..7a42e0d8445f 100644 --- a/arch/arm64/boot/dts/marvell/Makefile +++ b/arch/arm64/boot/dts/marvell/Makefile @@ -9,6 +9,3 @@ dtb-$(CONFIG_ARCH_MVEBU) += armada-7040-db.dtb dtb-$(CONFIG_ARCH_MVEBU) += armada-8040-db.dtb dtb-$(CONFIG_ARCH_MVEBU) += armada-8040-mcbin.dtb dtb-$(CONFIG_ARCH_MVEBU) += armada-8080-db.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/mediatek/Makefile b/arch/arm64/boot/dts/mediatek/Makefile index 80d174334014..f22501229c2e 100644 --- a/arch/arm64/boot/dts/mediatek/Makefile +++ b/arch/arm64/boot/dts/mediatek/Makefile @@ -4,6 +4,3 @@ dtb-$(CONFIG_ARCH_MEDIATEK) += mt6795-evb.dtb dtb-$(CONFIG_ARCH_MEDIATEK) += mt6797-evb.dtb dtb-$(CONFIG_ARCH_MEDIATEK) += mt7622-rfb1.dtb dtb-$(CONFIG_ARCH_MEDIATEK) += mt8173-evb.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/nvidia/Makefile b/arch/arm64/boot/dts/nvidia/Makefile index a9d51961bdca..1b69cfdc2d41 100644 --- a/arch/arm64/boot/dts/nvidia/Makefile +++ b/arch/arm64/boot/dts/nvidia/Makefile @@ -4,5 +4,3 @@ dtb-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-p2371-2180.dtb dtb-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-p2571.dtb dtb-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-smaug.dtb dtb-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186-p2771-0000.dtb - -always := $(dtb-y) diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile index 65af6f9ac569..7a65ef19a268 100644 --- a/arch/arm64/boot/dts/qcom/Makefile +++ b/arch/arm64/boot/dts/qcom/Makefile @@ -5,6 +5,3 @@ dtb-$(CONFIG_ARCH_QCOM) += msm8916-mtp.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8992-bullhead-rev-101.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8994-angler-rev-101.dtb dtb-$(CONFIG_ARCH_QCOM) += msm8996-mtp.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/realtek/Makefile b/arch/arm64/boot/dts/realtek/Makefile index 88cb515f7b21..6e2ae59a3745 100644 --- a/arch/arm64/boot/dts/realtek/Makefile +++ b/arch/arm64/boot/dts/realtek/Makefile @@ -1,4 +1 @@ dtb-$(CONFIG_ARCH_REALTEK) += rtd1295-zidoo-x9s.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/renesas/Makefile b/arch/arm64/boot/dts/renesas/Makefile index 960dadee8aed..5e0bb2854c33 100644 --- a/arch/arm64/boot/dts/renesas/Makefile +++ b/arch/arm64/boot/dts/renesas/Makefile @@ -3,5 +3,3 @@ dtb-$(CONFIG_ARCH_R8A7795) += r8a7795-salvator-xs.dtb dtb-$(CONFIG_ARCH_R8A7795) += r8a7795-es1-salvator-x.dtb r8a7795-es1-h3ulcb.dtb dtb-$(CONFIG_ARCH_R8A7796) += r8a7796-salvator-x.dtb r8a7796-m3ulcb.dtb dtb-$(CONFIG_ARCH_R8A77995) += r8a77995-draak.dtb - -always := $(dtb-y) diff --git a/arch/arm64/boot/dts/rockchip/Makefile b/arch/arm64/boot/dts/rockchip/Makefile index 6b6bb1db831c..573a041a220c 100644 --- a/arch/arm64/boot/dts/rockchip/Makefile +++ b/arch/arm64/boot/dts/rockchip/Makefile @@ -10,6 +10,3 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-firefly.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-gru-kevin.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-puma-haikou.dtb dtb-$(CONFIG_ARCH_ROCKCHIP) += rk3399-sapphire-excavator.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/socionext/Makefile b/arch/arm64/boot/dts/socionext/Makefile index 5eed3cecbc16..a0423165d9b6 100644 --- a/arch/arm64/boot/dts/socionext/Makefile +++ b/arch/arm64/boot/dts/socionext/Makefile @@ -4,5 +4,3 @@ dtb-$(CONFIG_ARCH_UNIPHIER) += \ uniphier-ld20-global.dtb \ uniphier-ld20-ref.dtb \ uniphier-pxs3-ref.dtb - -always := $(dtb-y) diff --git a/arch/arm64/boot/dts/sprd/Makefile b/arch/arm64/boot/dts/sprd/Makefile index c91b62e115dc..38cefdb115a8 100644 --- a/arch/arm64/boot/dts/sprd/Makefile +++ b/arch/arm64/boot/dts/sprd/Makefile @@ -1,5 +1,2 @@ dtb-$(CONFIG_ARCH_SPRD) += sc9836-openphone.dtb \ sp9860g-1h10.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/xilinx/Makefile b/arch/arm64/boot/dts/xilinx/Makefile index 74e195650f04..a2d67084a514 100644 --- a/arch/arm64/boot/dts/xilinx/Makefile +++ b/arch/arm64/boot/dts/xilinx/Makefile @@ -1,4 +1 @@ dtb-$(CONFIG_ARCH_ZYNQMP) += zynqmp-ep108.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/arm64/boot/dts/zte/Makefile b/arch/arm64/boot/dts/zte/Makefile index 71e07089cde0..14a1cdfc1559 100644 --- a/arch/arm64/boot/dts/zte/Makefile +++ b/arch/arm64/boot/dts/zte/Makefile @@ -1,5 +1,2 @@ dtb-$(CONFIG_ARCH_ZX) += zx296718-evb.dtb dtb-$(CONFIG_ARCH_ZX) += zx296718-pcbox.dtb - -always := $(dtb-y) -subdir-y := $(dts-dirs) diff --git a/arch/h8300/boot/dts/Makefile b/arch/h8300/boot/dts/Makefile index 6f3fe4795518..ae3188472b3a 100644 --- a/arch/h8300/boot/dts/Makefile +++ b/arch/h8300/boot/dts/Makefile @@ -7,8 +7,3 @@ obj-y += $(BUILTIN_DTB) dtb-$(CONFIG_H8300H_SIM) := h8300h_sim.dtb dtb-$(CONFIG_H8S_SIM) := h8s_sim.dtb dtb-$(CONFIG_H8S_EDOSK2674) := edosk2674.dtb - -dtstree := $(srctree)/$(src) -dtb-$(CONFIG_OF_ALL_DTBS) := $(patsubst $(dtstree)/%.dts,%.dtb, $(wildcard $(dtstree)/*.dts)) - -always := $(dtb-y) diff --git a/arch/metag/boot/dts/Makefile b/arch/metag/boot/dts/Makefile index 83d5b883e1f7..0060147e12a6 100644 --- a/arch/metag/boot/dts/Makefile +++ b/arch/metag/boot/dts/Makefile @@ -12,9 +12,4 @@ endif dtb-$(CONFIG_METAG_BUILTIN_DTB) += $(builtindtb-y).dtb obj-$(CONFIG_METAG_BUILTIN_DTB) += $(builtindtb-y).dtb.o -dtstree := $(srctree)/$(src) -dtb-$(CONFIG_OF_ALL_DTBS) := $(patsubst $(dtstree)/%.dts,%.dtb, $(wildcard $(dtstree)/*.dts)) - .SECONDARY: $(obj)/$(builtindtb-y).dtb.S - -always += $(dtb-y) diff --git a/arch/mips/boot/dts/Makefile b/arch/mips/boot/dts/Makefile index 7891ffa487d6..bc4ce85db136 100644 --- a/arch/mips/boot/dts/Makefile +++ b/arch/mips/boot/dts/Makefile @@ -1,20 +1,14 @@ -dts-dirs += brcm -dts-dirs += cavium-octeon -dts-dirs += img -dts-dirs += ingenic -dts-dirs += lantiq -dts-dirs += mti -dts-dirs += netlogic -dts-dirs += ni -dts-dirs += pic32 -dts-dirs += qca -dts-dirs += ralink -dts-dirs += xilfpga +subdir-y += brcm +subdir-y += cavium-octeon +subdir-y += img +subdir-y += ingenic +subdir-y += lantiq +subdir-y += mti +subdir-y += netlogic +subdir-y += ni +subdir-y += pic32 +subdir-y += qca +subdir-y += ralink +subdir-y += xilfpga -obj-y := $(addsuffix /, $(dts-dirs)) - -dtstree := $(srctree)/$(src) -dtb-$(CONFIG_OF_ALL_DTBS) := $(patsubst $(dtstree)/%.dts,%.dtb, $(foreach d,$(dts-dirs), $(wildcard $(dtstree)/$(d)/*.dts))) - -always := $(dtb-y) -subdir-y := $(dts-dirs) +obj-$(CONFIG_BUILTIN_DTB) := $(addsuffix /, $(subdir-y)) diff --git a/arch/mips/boot/dts/brcm/Makefile b/arch/mips/boot/dts/brcm/Makefile index ad76130e8ca0..bacb13159857 100644 --- a/arch/mips/boot/dts/brcm/Makefile +++ b/arch/mips/boot/dts/brcm/Makefile @@ -37,5 +37,3 @@ obj-y += $(patsubst %.dtb, %.dtb.o, $(dtb-y)) # Force kbuild to make empty built-in.o if necessary obj- += dummy.o - -always := $(dtb-y) diff --git a/arch/mips/boot/dts/cavium-octeon/Makefile b/arch/mips/boot/dts/cavium-octeon/Makefile index a6fb331de0ff..e9592a995349 100644 --- a/arch/mips/boot/dts/cavium-octeon/Makefile +++ b/arch/mips/boot/dts/cavium-octeon/Makefile @@ -4,5 +4,3 @@ obj-y += $(patsubst %.dtb, %.dtb.o, $(dtb-y)) # Force kbuild to make empty built-in.o if necessary obj- += dummy.o - -always := $(dtb-y) diff --git a/arch/mips/boot/dts/img/Makefile b/arch/mips/boot/dts/img/Makefile index 135f98761671..a46d77390aed 100644 --- a/arch/mips/boot/dts/img/Makefile +++ b/arch/mips/boot/dts/img/Makefile @@ -5,5 +5,3 @@ obj-$(CONFIG_MACH_PISTACHIO) += pistachio_marduk.dtb.o # Force kbuild to make empty built-in.o if necessary obj- += dummy.o - -always := $(dtb-y) diff --git a/arch/mips/boot/dts/ingenic/Makefile b/arch/mips/boot/dts/ingenic/Makefile index e3d0ec1bf577..ddd0fafd120e 100644 --- a/arch/mips/boot/dts/ingenic/Makefile +++ b/arch/mips/boot/dts/ingenic/Makefile @@ -5,5 +5,3 @@ obj-y += $(patsubst %.dtb, %.dtb.o, $(dtb-y)) # Force kbuild to make empty built-in.o if necessary obj- += dummy.o - -always := $(dtb-y) diff --git a/arch/mips/boot/dts/lantiq/Makefile b/arch/mips/boot/dts/lantiq/Makefile index 5976f089d038..586b1c9e580b 100644 --- a/arch/mips/boot/dts/lantiq/Makefile +++ b/arch/mips/boot/dts/lantiq/Makefile @@ -4,5 +4,3 @@ obj-y += $(patsubst %.dtb, %.dtb.o, $(dtb-y)) # Force kbuild to make empty built-in.o if necessary obj- += dummy.o - -always := $(dtb-y) diff --git a/arch/mips/boot/dts/mti/Makefile b/arch/mips/boot/dts/mti/Makefile index 9a1a6dc2ef36..faf7ac44725a 100644 --- a/arch/mips/boot/dts/mti/Makefile +++ b/arch/mips/boot/dts/mti/Makefile @@ -5,5 +5,3 @@ obj-y += $(patsubst %.dtb, %.dtb.o, $(dtb-y)) # Force kbuild to make empty built-in.o if necessary obj- += dummy.o - -always := $(dtb-y) diff --git a/arch/mips/boot/dts/netlogic/Makefile b/arch/mips/boot/dts/netlogic/Makefile index 6b2cf492db18..77ffb30c591b 100644 --- a/arch/mips/boot/dts/netlogic/Makefile +++ b/arch/mips/boot/dts/netlogic/Makefile @@ -8,5 +8,3 @@ obj-y += $(patsubst %.dtb, %.dtb.o, $(dtb-y)) # Force kbuild to make empty built-in.o if necessary obj- += dummy.o - -always := $(dtb-y) diff --git a/arch/mips/boot/dts/ni/Makefile b/arch/mips/boot/dts/ni/Makefile index 094da7219905..6cd9c606f025 100644 --- a/arch/mips/boot/dts/ni/Makefile +++ b/arch/mips/boot/dts/ni/Makefile @@ -2,5 +2,3 @@ dtb-$(CONFIG_FIT_IMAGE_FDT_NI169445) += 169445.dtb # Force kbuild to make empty built-in.o if necessary obj- += dummy.o - -always := $(dtb-y) diff --git a/arch/mips/boot/dts/pic32/Makefile b/arch/mips/boot/dts/pic32/Makefile index 0ee591b15720..5a08e48dbabb 100644 --- a/arch/mips/boot/dts/pic32/Makefile +++ b/arch/mips/boot/dts/pic32/Makefile @@ -7,5 +7,3 @@ obj-y += $(patsubst %.dtb, %.dtb.o, $(dtb-y)) # Force kbuild to make empty built-in.o if necessary obj- += dummy.o - -always := $(dtb-y) diff --git a/arch/mips/boot/dts/qca/Makefile b/arch/mips/boot/dts/qca/Makefile index 87cf351cff45..181db5d824ea 100644 --- a/arch/mips/boot/dts/qca/Makefile +++ b/arch/mips/boot/dts/qca/Makefile @@ -7,5 +7,3 @@ dtb-$(CONFIG_ATH79) += ar9331_tl_mr3020.dtb # Force kbuild to make empty built-in.o if necessary obj- += dummy.o - -always := $(dtb-y) diff --git a/arch/mips/boot/dts/ralink/Makefile b/arch/mips/boot/dts/ralink/Makefile index e0e3a9db8d76..7b64654c958c 100644 --- a/arch/mips/boot/dts/ralink/Makefile +++ b/arch/mips/boot/dts/ralink/Makefile @@ -9,5 +9,3 @@ obj-y += $(patsubst %.dtb, %.dtb.o, $(dtb-y)) # Force kbuild to make empty built-in.o if necessary obj- += dummy.o - -always := $(dtb-y) diff --git a/arch/mips/boot/dts/xilfpga/Makefile b/arch/mips/boot/dts/xilfpga/Makefile index 8b9ea11bf730..77c809645d75 100644 --- a/arch/mips/boot/dts/xilfpga/Makefile +++ b/arch/mips/boot/dts/xilfpga/Makefile @@ -4,5 +4,3 @@ obj-y += $(patsubst %.dtb, %.dtb.o, $(dtb-y)) # Force kbuild to make empty built-in.o if necessary obj- += dummy.o - -always := $(dtb-y) diff --git a/arch/xtensa/boot/dts/Makefile b/arch/xtensa/boot/dts/Makefile index c62dd6ca1f82..f8052ba5aea8 100644 --- a/arch/xtensa/boot/dts/Makefile +++ b/arch/xtensa/boot/dts/Makefile @@ -12,7 +12,6 @@ ifneq ($(CONFIG_BUILTIN_DTB),"") obj-$(CONFIG_OF) += $(BUILTIN_DTB) endif -dtstree := $(srctree)/$(src) -dtb-$(CONFIG_OF_ALL_DTBS) := $(patsubst $(dtstree)/%.dts,%.dtb, $(wildcard $(dtstree)/*.dts)) - -always += $(dtb-y) +# for CONFIG_OF_ALL_DTBS test +dtstree := $(srctree)/$(src) +dtb- := $(patsubst $(dtstree)/%.dts,%.dtb, $(wildcard $(dtstree)/*.dts)) diff --git a/scripts/Makefile.dtbinst b/scripts/Makefile.dtbinst index 993fb85982df..8e8fdfdc92a0 100644 --- a/scripts/Makefile.dtbinst +++ b/scripts/Makefile.dtbinst @@ -5,8 +5,6 @@ # INSTALL_DTBS_PATH directory or the default location: # # $INSTALL_PATH/dtbs/$KERNELRELEASE -# -# Traverse through subdirectories listed in $(dts-dirs). # ========================================================================== src := $(obj) @@ -20,8 +18,8 @@ include include/config/auto.conf include scripts/Kbuild.include include $(src)/Makefile -dtbinst-files := $(dtb-y) -dtbinst-dirs := $(dts-dirs) +dtbinst-files := $(sort $(dtb-y) $(if $(CONFIG_OF_ALL_DTBS), $(dtb-))) +dtbinst-dirs := $(subdir-y) $(subdir-m) # Helper targets for Installing DTBs into the boot directory quiet_cmd_dtb_install = INSTALL $< diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 5e975fee0f5b..09ec69d2c499 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -69,6 +69,11 @@ obj-dirs := $(dir $(multi-objs) $(obj-y)) real-objs-y := $(foreach m, $(filter-out $(subdir-obj-y), $(obj-y)), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))),$($(m:.o=-objs)) $($(m:.o=-y)),$(m))) $(extra-y) real-objs-m := $(foreach m, $(obj-m), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y)) $($(m:.o=-m))),$($(m:.o=-objs)) $($(m:.o=-y)) $($(m:.o=-m)),$(m))) +# DTB +# If CONFIG_OF_ALL_DTBS is enabled, all DT blobs are built +extra-y += $(dtb-y) +extra-$(CONFIG_OF_ALL_DTBS) += $(dtb-) + # Add subdir path extra-y := $(addprefix $(obj)/,$(extra-y)) -- cgit v1.3-6-gb490 From a7d34df3d12c34304638bbe7375d91c63717c453 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 11 Oct 2017 12:52:29 +0900 Subject: kbuild: add forward declaration of default target to Makefile.asm-generic $(kbuild-file) and Kbuild.include are included before the default target "all". We will add a target into Kbuild.include. In advance, add a forward declaration of the default target. Signed-off-by: Masahiro Yamada Reviewed-by: Douglas Anderson --- scripts/Makefile.asm-generic | 3 +++ 1 file changed, 3 insertions(+) (limited to 'scripts') diff --git a/scripts/Makefile.asm-generic b/scripts/Makefile.asm-generic index a6c8c1780855..9563215a23c6 100644 --- a/scripts/Makefile.asm-generic +++ b/scripts/Makefile.asm-generic @@ -5,6 +5,9 @@ # and for each file listed in this file with generic-y creates # a small wrapper file in $(obj) (arch/$(SRCARCH)/include/generated/$(src)) +PHONY := all +all: + kbuild-file := $(srctree)/arch/$(SRCARCH)/include/$(src)/Kbuild -include $(kbuild-file) -- cgit v1.3-6-gb490 From 3298b690b21cdbe6b2ae8076d9147027f396f2b1 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 16 Oct 2017 10:12:45 -0700 Subject: kbuild: Add a cache for generated variables While timing a "no-op" build of the kernel (incrementally building the kernel even though nothing changed) in the Chrome OS build system I found that it was much slower than I expected. Digging into things a bit, I found that quite a bit of the time was spent invoking the C compiler even though we weren't actually building anything. Currently in the Chrome OS build system the C compiler is called through a number of wrappers (one of which is written in python!) and can take upwards of 100 ms to invoke even if we're not doing anything difficult, so these invocations of the compiler were taking a lot of time. Worse the invocations couldn't seem to take advantage of the multiple cores on my system. Certainly it seems like we could make the compiler invocations in the Chrome OS build system faster, but only to a point. Inherently invoking a program as big as a C compiler is a fairly heavy operation. Thus even if we can speed the compiler calls it made sense to track down what was happening. It turned out that all the compiler invocations were coming from usages like this in the kernel's Makefile: KBUILD_CFLAGS += $(call cc-option,-fno-delete-null-pointer-checks,) Due to the way cc-option and similar statements work the above contains an implicit call to the C compiler. ...and due to the fact that we're storing the result in KBUILD_CFLAGS, a simply expanded variable, the call will happen every time the Makefile is parsed, even if there are no users of KBUILD_CFLAGS. Rather than redoing this computation every time, it makes a lot of sense to cache the result of all of the Makefile's compiler calls just like we do when we compile a ".c" file to a ".o" file. Conceptually this is quite a simple idea. ...and since the calls to invoke the compiler and similar tools are centrally located in the Kbuild.include file this doesn't even need to be super invasive. Implementing the cache in a simple-to-use and efficient way is not quite as simple as it first sounds, though. To get maximum speed we really want the cache in a format that make can natively understand and make doesn't really have an ability to load/parse files. ...but make _can_ import other Makefiles, so the solution is to store the cache in Makefile format. This requires coming up with a valid/unique Makefile variable name for each value to be cached, but that's solvable with some cleverness. After this change, we'll automatically create a ".cache.mk" file that will contain our cached variables. We'll load this on each invocation of make and will avoid recomputing anything that's already in our cache. The cache is stored in a format that it shouldn't need any invalidation since anything that might change should affect the "key" and any old cached value won't be used. Signed-off-by: Douglas Anderson Tested-by: Ingo Molnar Tested-by: Guenter Roeck Signed-off-by: Masahiro Yamada --- Makefile | 1 + scripts/Kbuild.include | 90 ++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 77 insertions(+), 14 deletions(-) (limited to 'scripts') diff --git a/Makefile b/Makefile index 1254719958ca..5be2c8d2ce73 100644 --- a/Makefile +++ b/Makefile @@ -1545,6 +1545,7 @@ clean: $(clean-dirs) -o -name '.*.d' -o -name '.*.tmp' -o -name '*.mod.c' \ -o -name '*.symtypes' -o -name 'modules.order' \ -o -name modules.builtin -o -name '.tmp_*.o.*' \ + -o -name .cache.mk \ -o -name '*.c.[012]*.*' \ -o -name '*.ll' \ -o -name '*.gcno' \) -type f -print | xargs rm -f diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include index 9ffd3dda3889..dfadb1c94368 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -8,6 +8,8 @@ squote := ' empty := space := $(empty) $(empty) space_escape := _-_SPACE_-_ +right_paren := ) +left_paren := ( ### # Name of target with a '.' as filename prefix. foo/bar.o => foo/.bar.o @@ -80,6 +82,60 @@ cc-cross-prefix = \ echo $(c); \ fi))) +# Tools for caching Makefile variables that are "expensive" to compute. +# +# Here we want to help deal with variables that take a long time to compute +# by making it easy to store these variables in a cache. +# +# The canonical example here is testing for compiler flags. On a simple system +# each call to the compiler takes 10 ms, but on a system with a compiler that's +# called through various wrappers it can take upwards of 100 ms. If we have +# 100 calls to the compiler this can take 1 second (on a simple system) or 10 +# seconds (on a complicated system). +# +# The "cache" will be in Makefile syntax and can be directly included. +# Any time we try to reference a variable that's not in the cache we'll +# calculate it and store it in the cache for next time. + +# Include values from last time +make-cache := $(if $(KBUILD_EXTMOD),$(KBUILD_EXTMOD)/,$(if $(obj),$(obj)/)).cache.mk +ifeq ($(wildcard $(dir $(make-cache))),) +$(shell mkdir -p '$(dir $(make-cache))') +endif +$(make-cache): ; +-include $(make-cache) + +# Usage: $(call __sanitize-opt,Hello=Hola$(comma)Goodbye Adios) +# +# Convert all '$', ')', '(', '\', '=', ' ', ',', ':' to '_' +__sanitize-opt = $(subst $$,_,$(subst $(right_paren),_,$(subst $(left_paren),_,$(subst \,_,$(subst =,_,$(subst $(space),_,$(subst $(comma),_,$(subst :,_,$(1))))))))) + +# Usage: $(call shell-cached,shell_command) +# Example: $(call shell-cached,md5sum /usr/bin/gcc) +# +# If we've already seen a call to this exact shell command (even in a +# previous invocation of make!) we'll return the value. If not, we'll +# compute it and store the result for future runs. +# +# This is a bit of voodoo, but basic explanation is that if the variable +# was undefined then we'll evaluate the shell command and store the result +# into the variable. We'll then store that value in the cache and finally +# output the value. +# +# NOTE: The $$(2) here isn't actually a parameter to __run-and-store. We +# happen to know that the caller will have their shell command in $(2) so the +# result of "call"ing this will produce a reference to that $(2). The reason +# for this strangeness is to avoid an extra level of eval (and escaping) of +# $(2). +define __run-and-store +ifeq ($(origin $(1)),undefined) + $$(eval $(1) := $$(shell $$(2))) + $$(shell echo '$(1) := $$($(1))' >> $(make-cache)) +endif +endef +__shell-cached = $(eval $(call __run-and-store,$(1)))$($(1)) +shell-cached = $(call __shell-cached,__cached_$(call __sanitize-opt,$(1)),$(1)) + # output directory for tests below TMPOUT := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/) @@ -87,30 +143,36 @@ TMPOUT := $(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/) # Usage: option = $(call try-run, $(CC)...-o "$$TMP",option-ok,otherwise) # Exit code chooses option. "$$TMP" serves as a temporary file and is # automatically cleaned up. -try-run = $(shell set -e; \ +__try-run = set -e; \ TMP="$(TMPOUT).$$$$.tmp"; \ TMPO="$(TMPOUT).$$$$.o"; \ if ($(1)) >/dev/null 2>&1; \ then echo "$(2)"; \ else echo "$(3)"; \ fi; \ - rm -f "$$TMP" "$$TMPO") + rm -f "$$TMP" "$$TMPO" + +try-run = $(shell $(__try-run)) + +# try-run-cached +# This works like try-run, but the result is cached. +try-run-cached = $(call shell-cached,$(__try-run)) # as-option # Usage: cflags-y += $(call as-option,-Wa$(comma)-isa=foo,) -as-option = $(call try-run,\ +as-option = $(call try-run-cached,\ $(CC) $(KBUILD_CFLAGS) $(1) -c -x assembler /dev/null -o "$$TMP",$(1),$(2)) # as-instr # Usage: cflags-y += $(call as-instr,instr,option1,option2) -as-instr = $(call try-run,\ +as-instr = $(call try-run-cached,\ printf "%b\n" "$(1)" | $(CC) $(KBUILD_AFLAGS) -c -x assembler -o "$$TMP" -,$(2),$(3)) # __cc-option # Usage: MY_CFLAGS += $(call __cc-option,$(CC),$(MY_CFLAGS),-march=winchip-c6,-march=i586) -__cc-option = $(call try-run,\ +__cc-option = $(call try-run-cached,\ $(1) -Werror $(2) $(3) -c -x c /dev/null -o "$$TMP",$(3),$(4)) # Do not attempt to build with gcc plugins during cc-option tests. @@ -130,23 +192,23 @@ hostcc-option = $(call __cc-option, $(HOSTCC),\ # cc-option-yn # Usage: flag := $(call cc-option-yn,-march=winchip-c6) -cc-option-yn = $(call try-run,\ +cc-option-yn = $(call try-run-cached,\ $(CC) -Werror $(KBUILD_CPPFLAGS) $(CC_OPTION_CFLAGS) $(1) -c -x c /dev/null -o "$$TMP",y,n) # cc-disable-warning # Usage: cflags-y += $(call cc-disable-warning,unused-but-set-variable) -cc-disable-warning = $(call try-run,\ +cc-disable-warning = $(call try-run-cached,\ $(CC) -Werror $(KBUILD_CPPFLAGS) $(CC_OPTION_CFLAGS) -W$(strip $(1)) -c -x c /dev/null -o "$$TMP",-Wno-$(strip $(1))) # cc-name # Expands to either gcc or clang -cc-name = $(shell $(CC) -v 2>&1 | grep -q "clang version" && echo clang || echo gcc) +cc-name = $(call shell-cached,$(CC) -v 2>&1 | grep -q "clang version" && echo clang || echo gcc) # cc-version -cc-version = $(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh $(CC)) +cc-version = $(call shell-cached,$(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh $(CC)) # cc-fullversion -cc-fullversion = $(shell $(CONFIG_SHELL) \ +cc-fullversion = $(call shell-cached,$(CONFIG_SHELL) \ $(srctree)/scripts/gcc-version.sh -p $(CC)) # cc-ifversion @@ -159,22 +221,22 @@ cc-if-fullversion = $(shell [ $(cc-fullversion) $(1) $(2) ] && echo $(3) || echo # cc-ldoption # Usage: ldflags += $(call cc-ldoption, -Wl$(comma)--hash-style=both) -cc-ldoption = $(call try-run,\ +cc-ldoption = $(call try-run-cached,\ $(CC) $(1) -nostdlib -x c /dev/null -o "$$TMP",$(1),$(2)) # ld-option # Usage: LDFLAGS += $(call ld-option, -X) -ld-option = $(call try-run,\ +ld-option = $(call try-run-cached,\ $(CC) -x c /dev/null -c -o "$$TMPO" ; $(LD) $(1) "$$TMPO" -o "$$TMP",$(1),$(2)) # ar-option # Usage: KBUILD_ARFLAGS := $(call ar-option,D) # Important: no spaces around options -ar-option = $(call try-run, $(AR) rc$(1) "$$TMP",$(1),$(2)) +ar-option = $(call try-run-cached, $(AR) rc$(1) "$$TMP",$(1),$(2)) # ld-version # Note this is mainly for HJ Lu's 3 number binutil versions -ld-version = $(shell $(LD) --version | $(srctree)/scripts/ld-version.sh) +ld-version = $(call shell-cached,$(LD) --version | $(srctree)/scripts/ld-version.sh) # ld-ifversion # Usage: $(call ld-ifversion, -ge, 22252, y) -- cgit v1.3-6-gb490 From e17c400ae194945eef9d9cae38a321c92c1986fb Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 13 Oct 2017 20:25:29 +0900 Subject: kbuild: shrink .cache.mk when it exceeds 1000 lines The cache files are only cleaned away by "make clean". If you continue incremental builds, the cache files will grow up little by little. It is not a big deal in general use cases because compiler flags do not change quite often. However, if you do build-test for various architectures, compilers, and kernel configurations, you will end up with huge cache files soon. When the cache file exceeds 1000 lines, shrink it down to 500 by "tail". The Least Recently Added lines are cut. (not Least Recently Used) I hope it will work well enough. Signed-off-by: Masahiro Yamada Reviewed-by: Douglas Anderson --- scripts/Kbuild.include | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'scripts') diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include index dfadb1c94368..064f477dfdca 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -105,6 +105,12 @@ endif $(make-cache): ; -include $(make-cache) +# If cache exceeds 1000 lines, shrink it down to 500. +ifneq ($(word 1000,$(filter __cached_%, $(.VARIABLES))),) +$(shell tail -n 500 $(make-cache) > $(make-cache).tmp; \ + mv $(make-cache).tmp $(make-cache)) +endif + # Usage: $(call __sanitize-opt,Hello=Hola$(comma)Goodbye Adios) # # Convert all '$', ')', '(', '\', '=', ' ', ',', ':' to '_' -- cgit v1.3-6-gb490 From 86a9df597cdd564d2d29c65897bcad42519e3678 Mon Sep 17 00:00:00 2001 From: Nick Desaulniers Date: Mon, 6 Nov 2017 10:47:54 -0800 Subject: kbuild: fix linker feature test macros when cross compiling with Clang I was not seeing my linker flags getting added when using ld-option when cross compiling with Clang. Upon investigation, this seems to be due to a difference in how GCC vs Clang handle cross compilation. GCC is configured at build time to support one backend, that is implicit when compiling. Clang is explicit via the use of `-target ` and ships with all supported backends by default. GNU Make feature test macros that compile then link will always fail when cross compiling with Clang unless Clang's triple is passed along to the compiler. For example: $ clang -x c /dev/null -c -o temp.o $ aarch64-linux-android/bin/ld -E temp.o aarch64-linux-android/bin/ld: unknown architecture of input file `temp.o' is incompatible with aarch64 output aarch64-linux-android/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400078 $ echo $? 1 $ clang -target aarch64-linux-android- -x c /dev/null -c -o temp.o $ aarch64-linux-android/bin/ld -E temp.o aarch64-linux-android/bin/ld: warning: cannot find entry symbol _start; defaulting to 00000000004002e4 $ echo $? 0 This causes conditional checks that invoke $(CC) without the target triple, then $(LD) on the result, to always fail. Suggested-by: Masahiro Yamada Signed-off-by: Nick Desaulniers Reviewed-by: Matthias Kaehlcke Signed-off-by: Masahiro Yamada --- scripts/Kbuild.include | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include index 064f477dfdca..be1c9d65eaf4 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -228,12 +228,13 @@ cc-if-fullversion = $(shell [ $(cc-fullversion) $(1) $(2) ] && echo $(3) || echo # cc-ldoption # Usage: ldflags += $(call cc-ldoption, -Wl$(comma)--hash-style=both) cc-ldoption = $(call try-run-cached,\ - $(CC) $(1) -nostdlib -x c /dev/null -o "$$TMP",$(1),$(2)) + $(CC) $(1) $(KBUILD_CPPFLAGS) $(CC_OPTION_CFLAGS) -nostdlib -x c /dev/null -o "$$TMP",$(1),$(2)) # ld-option # Usage: LDFLAGS += $(call ld-option, -X) ld-option = $(call try-run-cached,\ - $(CC) -x c /dev/null -c -o "$$TMPO" ; $(LD) $(1) "$$TMPO" -o "$$TMP",$(1),$(2)) + $(CC) $(KBUILD_CPPFLAGS) $(CC_OPTION_CFLAGS) -x c /dev/null -c -o "$$TMPO"; \ + $(LD) $(LDFLAGS) $(1) "$$TMPO" -o "$$TMP",$(1),$(2)) # ar-option # Usage: KBUILD_ARFLAGS := $(call ar-option,D) -- cgit v1.3-6-gb490 From 7e5758f7f74a591b52c6e8a8cfe82e6288ddced0 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Wed, 8 Nov 2017 11:01:59 +1100 Subject: leaking_addresses: use tabs instead of spaces Current code uses spaces instead of tabs in places. Use tabs instead of spaces. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 54 ++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 27 deletions(-) (limited to 'scripts') diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index 2977371b2956..b64efcecbb5e 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -170,46 +170,46 @@ sub push_to_global sub is_false_positive { - my ($match) = @_; + my ($match) = @_; + + if ($match =~ '\b(0x)?(f|F){16}\b' or + $match =~ '\b(0x)?0{16}\b') { + return 1; + } - if ($match =~ '\b(0x)?(f|F){16}\b' or - $match =~ '\b(0x)?0{16}\b') { - return 1; - } - # vsyscall memory region, we should probably check against a range here. - if ($match =~ '\bf{10}600000\b' or - $match =~ '\bf{10}601000\b') { - return 1; - } + if ($match =~ '\bf{10}600000\b' or# vsyscall memory region, we should probably check against a range here. + $match =~ '\bf{10}601000\b') { + return 1; + } - return 0; + return 0; } # True if argument potentially contains a kernel address. sub may_leak_address { - my ($line) = @_; - my $address = '\b(0x)?ffff[[:xdigit:]]{12}\b'; + my ($line) = @_; + my $address = '\b(0x)?ffff[[:xdigit:]]{12}\b'; - # Signal masks. - if ($line =~ '^SigBlk:' or - $line =~ '^SigCgt:') { - return 0; - } + # Signal masks. + if ($line =~ '^SigBlk:' or + $line =~ '^SigCgt:') { + return 0; + } - if ($line =~ '\bKEY=[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b' or - $line =~ '\b[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b') { + if ($line =~ '\bKEY=[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b' or + $line =~ '\b[[:xdigit:]]{14} [[:xdigit:]]{16} [[:xdigit:]]{16}\b') { return 0; - } + } - while (/($address)/g) { - if (!is_false_positive($1)) { - return 1; - } - } + while (/($address)/g) { + if (!is_false_positive($1)) { + return 1; + } + } - return 0; + return 0; } sub parse_dmesg -- cgit v1.3-6-gb490 From fa31a58202c5d9ebb26f562913b17e81357fe0e7 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Wed, 8 Nov 2017 11:04:27 +1100 Subject: leaking_addresses: remove dead/unused code debug_arrays is not called. Also, %seen hash is not used. We should remove unused code. Remove dead code. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 9 --------- 1 file changed, 9 deletions(-) (limited to 'scripts') diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index b64efcecbb5e..94b22d5b9237 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -133,14 +133,6 @@ walk(@DIRS); exit 0; -sub debug_arrays -{ - print 'dirs_any: ' . join(", ", @skip_walk_dirs_any) . "\n"; - print 'dirs_abs: ' . join(", ", @skip_walk_dirs_abs) . "\n"; - print 'parse_any: ' . join(", ", @skip_parse_files_any) . "\n"; - print 'parse_abs: ' . join(", ", @skip_parse_files_abs) . "\n"; -} - sub dprint { printf(STDERR @_) if $debug; @@ -281,7 +273,6 @@ sub skip_walk sub walk { my @dirs = @_; - my %seen; while (my $pwd = shift @dirs) { next if (skip_walk($pwd)); -- cgit v1.3-6-gb490 From ecd39dbd27d6f2907630678cbff464374edff8fe Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Wed, 8 Nov 2017 11:11:09 +1100 Subject: leaking_addresses: remove command line options Currently script accepts files to skip. This was added to make running the script faster (for repeat runs). We can remove this functionality in preparation for adding sub commands (scan and format) to the script. Remove command line options. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 58 -------------------------------------------- 1 file changed, 58 deletions(-) (limited to 'scripts') diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index 94b22d5b9237..719ed0aaede7 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -7,25 +7,6 @@ # - Scans dmesg output. # - Walks directory tree and parses each file (for each directory in @DIRS). # -# You can configure the behaviour of the script; -# -# - By adding paths, for directories you do not want to walk; -# absolute paths: @skip_walk_dirs_abs -# directory names: @skip_walk_dirs_any -# -# - By adding paths, for files you do not want to parse; -# absolute paths: @skip_parse_files_abs -# file names: @skip_parse_files_any -# -# The use of @skip_xxx_xxx_any causes files to be skipped where ever they occur. -# For example adding 'fd' to @skip_walk_dirs_any causes the fd/ directory to be -# skipped for all PID sub-directories of /proc -# -# The same thing can be achieved by passing command line options to --dont-walk -# and --dont-parse. If absolute paths are supplied to these options they are -# appended to the @skip_xxx_xxx_abs arrays. If file names are supplied to these -# options, they are appended to the @skip_xxx_xxx_any arrays. -# # Use --debug to output path before parsing, this is useful to find files that # cause the script to choke. # @@ -50,8 +31,6 @@ my @DIRS = ('/proc', '/sys'); # Command line options. my $help = 0; my $debug = 0; -my @dont_walk = (); -my @dont_parse = (); # Do not parse these files (absolute path). my @skip_parse_files_abs = ('/proc/kmsg', @@ -96,20 +75,9 @@ Version: $V Options: - --dont-walk= Don't walk tree starting at . - --dont-parse= Don't parse . -d, --debug Display debugging output. -h, --help, --version Display this help and exit. -If an absolute path is passed to --dont_XXX then this path is skipped. If a -single filename is passed then this file/directory will be skipped when -appearing under any subdirectory. - -Example: - - # Just scan dmesg output. - scripts/leaking_addresses.pl --dont_walk_abs /proc --dont_walk_abs /sys - Scans the running (64 bit) kernel for potential leaking addresses. EOM @@ -117,8 +85,6 @@ EOM } GetOptions( - 'dont-walk=s' => \@dont_walk, - 'dont-parse=s' => \@dont_parse, 'd|debug' => \$debug, 'h|help' => \$help, 'version' => \$help @@ -126,8 +92,6 @@ GetOptions( help(0) if ($help); -push_to_global(); - parse_dmesg(); walk(@DIRS); @@ -138,28 +102,6 @@ sub dprint printf(STDERR @_) if $debug; } -sub push_in_abs_any -{ - my ($in, $abs, $any) = @_; - - foreach my $path (@$in) { - if (File::Spec->file_name_is_absolute($path)) { - push @$abs, $path; - } elsif (index($path,'/') == -1) { - push @$any, $path; - } else { - print 'path error: ' . $path; - } - } -} - -# Push command line options to global arrays. -sub push_to_global -{ - push_in_abs_any(\@dont_walk, \@skip_walk_dirs_abs, \@skip_walk_dirs_any); - push_in_abs_any(\@dont_parse, \@skip_parse_files_abs, \@skip_parse_files_any); -} - sub is_false_positive { my ($match) = @_; -- cgit v1.3-6-gb490 From a284733e26e8e173cb5f589531a655d723ecb3ea Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 9 Nov 2017 13:28:43 +1100 Subject: leaking_addresses: fix comment string typo Fix typo in comment string. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index 719ed0aaede7..3f8c6e230962 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -40,7 +40,7 @@ my @skip_parse_files_abs = ('/proc/kmsg', '/sys/kernel/debug/tracing/trace_pipe', '/sys/kernel/security/apparmor/revision'); -# Do not parse thes files under any subdirectory. +# Do not parse these files under any subdirectory. my @skip_parse_files_any = ('0', '1', '2', -- cgit v1.3-6-gb490 From 1c1e3be0bf37db1396b4ecd995992643a6d92c00 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 9 Nov 2017 14:02:41 +1100 Subject: leaking_addresses: add to exclude files/paths list There are a couple more files that cause the script to stall. /sys/firmware/devicetree and its symlink /proc/device-tree, reported by Michael Ellerman. usbmon should be skipped were ever it appears. Reported by Kees Cook Add files to be excluded from parsing. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 3 +++ 1 file changed, 3 insertions(+) (limited to 'scripts') diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index 3f8c6e230962..0aac03a020a8 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -37,6 +37,8 @@ my @skip_parse_files_abs = ('/proc/kmsg', '/proc/kcore', '/proc/fs/ext4/sdb1/mb_groups', '/proc/1/fd/3', + '/sys/firmware/devicetree', + '/proc/device-tree', '/sys/kernel/debug/tracing/trace_pipe', '/sys/kernel/security/apparmor/revision'); @@ -61,6 +63,7 @@ my @skip_walk_dirs_any = ('self', 'thread-self', 'cwd', 'fd', + 'usbmon', 'stderr', 'stdin', 'stdout'); -- cgit v1.3-6-gb490 From d09bd8da8812a4df69ea3303e6df846a729ec623 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 9 Nov 2017 15:07:15 +1100 Subject: leaking_addresses: add summary reporting options Currently script just dumps all results found. Potentially, this risks losing single results among multiple duplicate results. We need some way of restricting duplicates to assist users of the script. It would also be nice if we got a report instead of raw results. Duplicates can be defined in various ways, instead of trying to find a single perfect solution we can present the user with various options to display the output. Doing so will typically lead to users wanting to view the output multiple times. Currently we scan the kernel each time, this is slow and unnecessary. We can expedite the process by writing the results to file for subsequent viewing. Add command line options to enable summary reporting, including options to write to and read from file. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 191 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 188 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index 0aac03a020a8..4610ad3c80c2 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -31,6 +31,13 @@ my @DIRS = ('/proc', '/sys'); # Command line options. my $help = 0; my $debug = 0; +my $raw = 0; +my $output_raw = ""; # Write raw results to file. +my $input_raw = ""; # Read raw results from file instead of scanning. + +my $suppress_dmesg = 0; # Don't show dmesg in output. +my $squash_by_path = 0; # Summary report grouped by absolute path. +my $squash_by_filename = 0; # Summary report grouped by filename. # Do not parse these files (absolute path). my @skip_parse_files_abs = ('/proc/kmsg', @@ -73,13 +80,31 @@ sub help my ($exitcode) = @_; print << "EOM"; + Usage: $P [OPTIONS] Version: $V Options: - -d, --debug Display debugging output. - -h, --help, --version Display this help and exit. + -o, --output-raw= Save results for future processing. + -i, --input-raw= Read results from file instead of scanning. + --raw Show raw results (default). + --suppress-dmesg Do not show dmesg results. + --squash-by-path Show one result per unique path. + --squash-by-filename Show one result per unique filename. + -d, --debug Display debugging output. + -h, --help, --version Display this help and exit. + +Examples: + + # Scan kernel and dump raw results. + $0 + + # Scan kernel and save results to file. + $0 --output-raw scan.out + + # View summary report. + $0 --input-raw scan.out --squash-by-filename Scans the running (64 bit) kernel for potential leaking addresses. @@ -90,11 +115,33 @@ EOM GetOptions( 'd|debug' => \$debug, 'h|help' => \$help, - 'version' => \$help + 'version' => \$help, + 'o|output-raw=s' => \$output_raw, + 'i|input-raw=s' => \$input_raw, + 'suppress-dmesg' => \$suppress_dmesg, + 'squash-by-path' => \$squash_by_path, + 'squash-by-filename' => \$squash_by_filename, + 'raw' => \$raw, ) or help(1); help(0) if ($help); +if ($input_raw) { + format_output($input_raw); + exit(0); +} + +if (!$input_raw and ($squash_by_path or $squash_by_filename)) { + printf "\nSummary reporting only available with --input-raw=\n"; + printf "(First run scan with --output-raw=.)\n"; + exit(128); +} + +if ($output_raw) { + open my $fh, '>', $output_raw or die "$0: $output_raw: $!\n"; + select $fh; +} + parse_dmesg(); walk(@DIRS); @@ -239,3 +286,141 @@ sub walk } } } + +sub format_output +{ + my ($file) = @_; + + # Default is to show raw results. + if ($raw or (!$squash_by_path and !$squash_by_filename)) { + dump_raw_output($file); + return; + } + + my ($total, $dmesg, $paths, $files) = parse_raw_file($file); + + printf "\nTotal number of results from scan (incl dmesg): %d\n", $total; + + if (!$suppress_dmesg) { + print_dmesg($dmesg); + } + + if ($squash_by_filename) { + squash_by($files, 'filename'); + } + + if ($squash_by_path) { + squash_by($paths, 'path'); + } +} + +sub dump_raw_output +{ + my ($file) = @_; + + open (my $fh, '<', $file) or die "$0: $file: $!\n"; + while (<$fh>) { + if ($suppress_dmesg) { + if ("dmesg:" eq substr($_, 0, 6)) { + next; + } + } + print $_; + } + close $fh; +} + +sub parse_raw_file +{ + my ($file) = @_; + + my $total = 0; # Total number of lines parsed. + my @dmesg; # dmesg output. + my %files; # Unique filenames containing leaks. + my %paths; # Unique paths containing leaks. + + open (my $fh, '<', $file) or die "$0: $file: $!\n"; + while (my $line = <$fh>) { + $total++; + + if ("dmesg:" eq substr($line, 0, 6)) { + push @dmesg, $line; + next; + } + + cache_path(\%paths, $line); + cache_filename(\%files, $line); + } + + return $total, \@dmesg, \%paths, \%files; +} + +sub print_dmesg +{ + my ($dmesg) = @_; + + print "\ndmesg output:\n"; + + if (@$dmesg == 0) { + print "\n"; + return; + } + + foreach(@$dmesg) { + my $index = index($_, ': '); + $index += 2; # skid ': ' + print substr($_, $index); + } +} + +sub squash_by +{ + my ($ref, $desc) = @_; + + print "\nResults squashed by $desc (excl dmesg). "; + print "Displaying [ <$desc>], \n"; + + if (keys %$ref == 0) { + print "\n"; + return; + } + + foreach(keys %$ref) { + my $lines = $ref->{$_}; + my $length = @$lines; + printf "[%d %s] %s", $length, $_, @$lines[0]; + } +} + +sub cache_path +{ + my ($paths, $line) = @_; + + my $index = index($line, ': '); + my $path = substr($line, 0, $index); + + $index += 2; # skip ': ' + add_to_cache($paths, $path, substr($line, $index)); +} + +sub cache_filename +{ + my ($files, $line) = @_; + + my $index = index($line, ': '); + my $path = substr($line, 0, $index); + my $filename = basename($path); + + $index += 2; # skip ': ' + add_to_cache($files, $filename, substr($line, $index)); +} + +sub add_to_cache +{ + my ($cache, $key, $value) = @_; + + if (!$cache->{$key}) { + $cache->{$key} = (); + } + push @{$cache->{$key}}, $value; +} -- cgit v1.3-6-gb490 From 62139c1242b573cb647776e3abc503a69fbd2c08 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 9 Nov 2017 15:19:40 +1100 Subject: leaking_addresses: add support for ppc64 Currently script is targeted at x86_64. We can support other architectures by using the correct regular expressions for each architecture. Add the infrastructure to support multiple architectures. Add support for ppc64. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 66 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 6 deletions(-) (limited to 'scripts') diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index 4610ad3c80c2..1d6ab7f1b10c 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -21,6 +21,7 @@ use File::Spec; use Cwd 'abs_path'; use Term::ANSIColor qw(:constants); use Getopt::Long qw(:config no_auto_abbrev); +use Config; my $P = $0; my $V = '0.01'; @@ -28,6 +29,11 @@ my $V = '0.01'; # Directories to scan. my @DIRS = ('/proc', '/sys'); +# Script can only grep for kernel addresses on the following architectures. If +# your architecture is not listed here and has a grep'able kernel address please +# consider submitting a patch. +my @SUPPORTED_ARCHITECTURES = ('x86_64', 'ppc64'); + # Command line options. my $help = 0; my $debug = 0; @@ -137,6 +143,20 @@ if (!$input_raw and ($squash_by_path or $squash_by_filename)) { exit(128); } +if (!is_supported_architecture()) { + printf "\nScript does not support your architecture, sorry.\n"; + printf "\nCurrently we support: \n\n"; + foreach(@SUPPORTED_ARCHITECTURES) { + printf "\t%s\n", $_; + } + + my $archname = $Config{archname}; + printf "\n\$ perl -MConfig -e \'print \"\$Config{archname}\\n\"\'\n"; + printf "%s\n", $archname; + + exit(129); +} + if ($output_raw) { open my $fh, '>', $output_raw or die "$0: $output_raw: $!\n"; select $fh; @@ -152,6 +172,31 @@ sub dprint printf(STDERR @_) if $debug; } +sub is_supported_architecture +{ + return (is_x86_64() or is_ppc64()); +} + +sub is_x86_64 +{ + my $archname = $Config{archname}; + + if ($archname =~ m/x86_64/) { + return 1; + } + return 0; +} + +sub is_ppc64 +{ + my $archname = $Config{archname}; + + if ($archname =~ m/powerpc/ and $archname =~ m/64/) { + return 1; + } + return 0; +} + sub is_false_positive { my ($match) = @_; @@ -161,10 +206,12 @@ sub is_false_positive return 1; } - - if ($match =~ '\bf{10}600000\b' or# vsyscall memory region, we should probably check against a range here. - $match =~ '\bf{10}601000\b') { - return 1; + if (is_x86_64) { + # vsyscall memory region, we should probably check against a range here. + if ($match =~ '\bf{10}600000\b' or + $match =~ '\bf{10}601000\b') { + return 1; + } } return 0; @@ -174,7 +221,7 @@ sub is_false_positive sub may_leak_address { my ($line) = @_; - my $address = '\b(0x)?ffff[[:xdigit:]]{12}\b'; + my $address_re; # Signal masks. if ($line =~ '^SigBlk:' or @@ -187,7 +234,14 @@ sub may_leak_address return 0; } - while (/($address)/g) { + # One of these is guaranteed to be true. + if (is_x86_64()) { + $address_re = '\b(0x)?ffff[[:xdigit:]]{12}\b'; + } elsif (is_ppc64()) { + $address_re = '\b(0x)?[89abcdef]00[[:xdigit:]]{13}\b'; + } + + while (/($address_re)/g) { if (!is_false_positive($1)) { return 1; } -- cgit v1.3-6-gb490 From dd98c252aea2a3dcd4014cb71bcdf9588519b800 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 9 Nov 2017 15:37:06 +1100 Subject: leaking_addresses: add timeout on file read Currently script can stall if we read certain files (like /proc/kmsg). While we have a mechanism to skip these files once they are discovered it would be nice to not stall on as yet undiscovered files of this kind. Set a timer before each file is parsed, warn user if timer expires. Suggested-by: Kees Cook Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index 1d6ab7f1b10c..6efd1fdb7d25 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -29,6 +29,9 @@ my $V = '0.01'; # Directories to scan. my @DIRS = ('/proc', '/sys'); +# Timer for parsing each file, in seconds. +my $TIMEOUT = 10; + # Script can only grep for kernel addresses on the following architectures. If # your architecture is not listed here and has a grep'able kernel address please # consider submitting a patch. @@ -284,6 +287,23 @@ sub skip_parse return skip($path, \@skip_parse_files_abs, \@skip_parse_files_any); } +sub timed_parse_file +{ + my ($file) = @_; + + eval { + local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required. + alarm $TIMEOUT; + parse_file($file); + alarm 0; + }; + + if ($@) { + die unless $@ eq "alarm\n"; # Propagate unexpected errors. + printf STDERR "timed out parsing: %s\n", $file; + } +} + sub parse_file { my ($file) = @_; @@ -335,7 +355,7 @@ sub walk if (-d $path) { push @dirs, $path; } else { - parse_file($path); + timed_parse_file($path); } } } -- cgit v1.3-6-gb490 From a11949ec20635b43d82ee229315fd2e3c80c22a3 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Tue, 14 Nov 2017 09:25:11 +1100 Subject: leaking_addresses: add SigIgn to false positives Signal masks are false positives, we already check for SigBlk and SigCgt but we missed SigIgn. Add SigIgn to false positive check. Signed-off-by: Tobin C. Harding --- scripts/leaking_addresses.pl | 1 + 1 file changed, 1 insertion(+) (limited to 'scripts') diff --git a/scripts/leaking_addresses.pl b/scripts/leaking_addresses.pl index 6efd1fdb7d25..bc5788000018 100755 --- a/scripts/leaking_addresses.pl +++ b/scripts/leaking_addresses.pl @@ -228,6 +228,7 @@ sub may_leak_address # Signal masks. if ($line =~ '^SigBlk:' or + $line =~ '^SigIgn:' or $line =~ '^SigCgt:') { return 0; } -- cgit v1.3-6-gb490 From 8c5d4b648b46b3f5b721b9aff021c9f639d42c35 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 30 Sep 2017 10:10:06 +0900 Subject: kbuild: rpm-pkg: refactor mkspec with here doc The repeat of echo is unreadable. The here-document is a well-known device for such scripts. One difficulty is we have a bunch of PREBUILT conditionals that would split the here-document. My idea is to add "$S" annotatation to lines only for the source package spec file, then post-process it by sed. I hope it will make our life easier than repeat of "cat < --- scripts/package/mkspec | 218 ++++++++++++++++++++++++------------------------- 1 file changed, 105 insertions(+), 113 deletions(-) (limited to 'scripts') diff --git a/scripts/package/mkspec b/scripts/package/mkspec index 97feb60e6482..b341d5d8e793 100755 --- a/scripts/package/mkspec +++ b/scripts/package/mkspec @@ -11,9 +11,9 @@ # how we were called determines which rpms we build and how we build them if [ "$1" = prebuilt ]; then - PREBUILT=true + S=DEL else - PREBUILT=false + S= fi if grep -q CONFIG_DRM=y .config; then @@ -26,120 +26,112 @@ EXCLUDES="$RCS_TAR_IGNORE --exclude=.tmp_versions --exclude=*vmlinux* \ --exclude=*.o --exclude=*.ko --exclude=*.cmd --exclude=Documentation \ --exclude=.config.old --exclude=.missing-syscalls.d" -echo "Name: kernel" -echo "Summary: The Linux Kernel" -echo "Version: $__KERNELRELEASE" -echo "Release: $(cat .version 2>/dev/null || echo 1)" -echo "License: GPL" -echo "Group: System Environment/Kernel" -echo "Vendor: The Linux Community" -echo "URL: http://www.kernel.org" +# We can label the here-doc lines for conditional output to the spec file +# +# Labels: +# $S: this line is enabled only when building source package +sed -e '/^DEL/d' -e 's/^\t*//' </dev/null || echo 1) + License: GPL + Group: System Environment/Kernel + Vendor: The Linux Community + URL: http://www.kernel.org +$S Source: kernel-$__KERNELRELEASE.tar.gz + Provides: $PROVIDES + %define __spec_install_post /usr/lib/rpm/brp-compress || : + %define debug_package %{nil} -if ! $PREBUILT; then -echo "Source: kernel-$__KERNELRELEASE.tar.gz" -fi + %description + The Linux Kernel, the operating system core itself -echo "Provides: $PROVIDES" -echo "%define __spec_install_post /usr/lib/rpm/brp-compress || :" -echo "%define debug_package %{nil}" -echo "" -echo "%description" -echo "The Linux Kernel, the operating system core itself" -echo "" -echo "%package headers" -echo "Summary: Header files for the Linux kernel for use by glibc" -echo "Group: Development/System" -echo "Obsoletes: kernel-headers" -echo "Provides: kernel-headers = %{version}" -echo "%description headers" -echo "Kernel-headers includes the C header files that specify the interface" -echo "between the Linux kernel and userspace libraries and programs. The" -echo "header files define structures and constants that are needed for" -echo "building most standard programs and are also needed for rebuilding the" -echo "glibc package." -echo "" + %package headers + Summary: Header files for the Linux kernel for use by glibc + Group: Development/System + Obsoletes: kernel-headers + Provides: kernel-headers = %{version} + %description headers + Kernel-headers includes the C header files that specify the interface + between the Linux kernel and userspace libraries and programs. The + header files define structures and constants that are needed for + building most standard programs and are also needed for rebuilding the + glibc package. -if ! $PREBUILT; then -echo "%package devel" -echo "Summary: Development package for building kernel modules to match the $__KERNELRELEASE kernel" -echo "Group: System Environment/Kernel" -echo "AutoReqProv: no" -echo "%description -n kernel-devel" -echo "This package provides kernel headers and makefiles sufficient to build modules" -echo "against the $__KERNELRELEASE kernel package." -echo "" -echo "%prep" -echo "%setup -q" -echo "" -echo "%build" -echo "make %{?_smp_mflags} KBUILD_BUILD_VERSION=%{release}" -echo "" -fi +$S %package devel +$S Summary: Development package for building kernel modules to match the $__KERNELRELEASE kernel +$S Group: System Environment/Kernel +$S AutoReqProv: no +$S %description -n kernel-devel +$S This package provides kernel headers and makefiles sufficient to build modules +$S against the $__KERNELRELEASE kernel package. +$S +$S %prep +$S %setup -q +$S +$S %build +$S make %{?_smp_mflags} KBUILD_BUILD_VERSION=%{release} +$S + %install + mkdir -p \$RPM_BUILD_ROOT/boot + %ifarch ia64 + mkdir -p \$RPM_BUILD_ROOT/boot/efi + cp \$(make image_name) \$RPM_BUILD_ROOT/boot/efi/vmlinuz-$KERNELRELEASE + ln -s efi/vmlinuz-$KERNELRELEASE \$RPM_BUILD_ROOT/boot/ + %else + cp \$(make image_name) \$RPM_BUILD_ROOT/boot/vmlinuz-$KERNELRELEASE + %endif + make %{?_smp_mflags} INSTALL_MOD_PATH=\$RPM_BUILD_ROOT KBUILD_SRC= modules_install + make %{?_smp_mflags} INSTALL_HDR_PATH=\$RPM_BUILD_ROOT/usr KBUILD_SRC= headers_install + cp System.map \$RPM_BUILD_ROOT/boot/System.map-$KERNELRELEASE + cp .config \$RPM_BUILD_ROOT/boot/config-$KERNELRELEASE + bzip2 -9 --keep vmlinux + mv vmlinux.bz2 \$RPM_BUILD_ROOT/boot/vmlinux-$KERNELRELEASE.bz2 +$S rm -f \$RPM_BUILD_ROOT/lib/modules/$KERNELRELEASE/build +$S rm -f \$RPM_BUILD_ROOT/lib/modules/$KERNELRELEASE/source +$S mkdir -p \$RPM_BUILD_ROOT/usr/src/kernels/$KERNELRELEASE +$S tar cf - . $EXCLUDES | tar xf - -C \$RPM_BUILD_ROOT/usr/src/kernels/$KERNELRELEASE +$S cd \$RPM_BUILD_ROOT/lib/modules/$KERNELRELEASE +$S ln -sf /usr/src/kernels/$KERNELRELEASE build +$S ln -sf /usr/src/kernels/$KERNELRELEASE source -echo "%install" -echo 'mkdir -p $RPM_BUILD_ROOT/boot' -echo "%ifarch ia64" -echo 'mkdir -p $RPM_BUILD_ROOT/boot/efi' -echo 'cp $(make image_name) $RPM_BUILD_ROOT'"/boot/efi/vmlinuz-$KERNELRELEASE" -echo 'ln -s '"efi/vmlinuz-$KERNELRELEASE" '$RPM_BUILD_ROOT'"/boot/" -echo "%else" -echo 'cp $(make image_name) $RPM_BUILD_ROOT'"/boot/vmlinuz-$KERNELRELEASE" -echo "%endif" -echo 'make %{?_smp_mflags} INSTALL_MOD_PATH=$RPM_BUILD_ROOT KBUILD_SRC= modules_install' -echo 'make %{?_smp_mflags} INSTALL_HDR_PATH=$RPM_BUILD_ROOT/usr KBUILD_SRC= headers_install' -echo 'cp System.map $RPM_BUILD_ROOT'"/boot/System.map-$KERNELRELEASE" -echo 'cp .config $RPM_BUILD_ROOT'"/boot/config-$KERNELRELEASE" -echo 'bzip2 -9 --keep vmlinux' -echo 'mv vmlinux.bz2 $RPM_BUILD_ROOT'"/boot/vmlinux-$KERNELRELEASE.bz2" + %clean + rm -rf \$RPM_BUILD_ROOT -if ! $PREBUILT; then -echo 'rm -f $RPM_BUILD_ROOT'"/lib/modules/$KERNELRELEASE/build" -echo 'rm -f $RPM_BUILD_ROOT'"/lib/modules/$KERNELRELEASE/source" -echo "mkdir -p "'$RPM_BUILD_ROOT'"/usr/src/kernels/$KERNELRELEASE" -echo "tar cf - . $EXCLUDES | tar xf - -C "'$RPM_BUILD_ROOT'"/usr/src/kernels/$KERNELRELEASE" -echo 'cd $RPM_BUILD_ROOT'"/lib/modules/$KERNELRELEASE" -echo "ln -sf /usr/src/kernels/$KERNELRELEASE build" -echo "ln -sf /usr/src/kernels/$KERNELRELEASE source" -fi + %post + if [ -x /sbin/installkernel -a -r /boot/vmlinuz-$KERNELRELEASE -a -r /boot/System.map-$KERNELRELEASE ]; then + cp /boot/vmlinuz-$KERNELRELEASE /boot/.vmlinuz-$KERNELRELEASE-rpm + cp /boot/System.map-$KERNELRELEASE /boot/.System.map-$KERNELRELEASE-rpm + rm -f /boot/vmlinuz-$KERNELRELEASE /boot/System.map-$KERNELRELEASE + /sbin/installkernel $KERNELRELEASE /boot/.vmlinuz-$KERNELRELEASE-rpm /boot/.System.map-$KERNELRELEASE-rpm + rm -f /boot/.vmlinuz-$KERNELRELEASE-rpm /boot/.System.map-$KERNELRELEASE-rpm + fi -echo "" -echo "%clean" -echo 'rm -rf $RPM_BUILD_ROOT' -echo "" -echo "%post" -echo "if [ -x /sbin/installkernel -a -r /boot/vmlinuz-$KERNELRELEASE -a -r /boot/System.map-$KERNELRELEASE ]; then" -echo "cp /boot/vmlinuz-$KERNELRELEASE /boot/.vmlinuz-$KERNELRELEASE-rpm" -echo "cp /boot/System.map-$KERNELRELEASE /boot/.System.map-$KERNELRELEASE-rpm" -echo "rm -f /boot/vmlinuz-$KERNELRELEASE /boot/System.map-$KERNELRELEASE" -echo "/sbin/installkernel $KERNELRELEASE /boot/.vmlinuz-$KERNELRELEASE-rpm /boot/.System.map-$KERNELRELEASE-rpm" -echo "rm -f /boot/.vmlinuz-$KERNELRELEASE-rpm /boot/.System.map-$KERNELRELEASE-rpm" -echo "fi" -echo "" -echo "%preun" -echo "if [ -x /sbin/new-kernel-pkg ]; then" -echo "new-kernel-pkg --remove $KERNELRELEASE --rminitrd --initrdfile=/boot/initramfs-$KERNELRELEASE.img" -echo "fi" -echo "" -echo "%postun" -echo "if [ -x /sbin/update-bootloader ]; then" -echo "/sbin/update-bootloader --remove $KERNELRELEASE" -echo "fi" -echo "" -echo "%files" -echo '%defattr (-, root, root)' -echo "/lib/modules/$KERNELRELEASE" -echo "%exclude /lib/modules/$KERNELRELEASE/build" -echo "%exclude /lib/modules/$KERNELRELEASE/source" -echo "/boot/*" -echo "" -echo "%files headers" -echo '%defattr (-, root, root)' -echo "/usr/include" -if ! $PREBUILT; then -echo "" -echo "%files devel" -echo '%defattr (-, root, root)' -echo "/usr/src/kernels/$KERNELRELEASE" -echo "/lib/modules/$KERNELRELEASE/build" -echo "/lib/modules/$KERNELRELEASE/source" -fi + %preun + if [ -x /sbin/new-kernel-pkg ]; then + new-kernel-pkg --remove $KERNELRELEASE --rminitrd --initrdfile=/boot/initramfs-$KERNELRELEASE.img + fi + + %postun + if [ -x /sbin/update-bootloader ]; then + /sbin/update-bootloader --remove $KERNELRELEASE + fi + + %files + %defattr (-, root, root) + /lib/modules/$KERNELRELEASE + %exclude /lib/modules/$KERNELRELEASE/build + %exclude /lib/modules/$KERNELRELEASE/source + /boot/* + + %files headers + %defattr (-, root, root) + /usr/include +$S +$S %files devel +$S %defattr (-, root, root) +$S /usr/src/kernels/$KERNELRELEASE +$S /lib/modules/$KERNELRELEASE/build +$S /lib/modules/$KERNELRELEASE/source +EOF -- cgit v1.3-6-gb490 From 0b7f12f5912de636a9c1671ee343f31f21c42b2f Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 30 Sep 2017 10:10:07 +0900 Subject: kbuild: rpm-pkg: fix build error when CONFIG_MODULES is disabled When CONFIG_MODULES is disabled, make rpm-pkg / binrpm-pkg fails with the following message: The present kernel configuration has modules disabled. Type 'make config' and enable loadable module support. Then build a kernel with module support enabled. Do not install modules in the case. Also, omit the devel package. Signed-off-by: Masahiro Yamada --- scripts/package/mkspec | 57 ++++++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 25 deletions(-) (limited to 'scripts') diff --git a/scripts/package/mkspec b/scripts/package/mkspec index b341d5d8e793..d352a0188770 100755 --- a/scripts/package/mkspec +++ b/scripts/package/mkspec @@ -16,6 +16,12 @@ else S= fi +if grep -q CONFIG_MODULES=y .config; then + M= +else + M=DEL +fi + if grep -q CONFIG_DRM=y .config; then PROVIDES=kernel-drm fi @@ -30,6 +36,7 @@ EXCLUDES="$RCS_TAR_IGNORE --exclude=.tmp_versions --exclude=*vmlinux* \ # # Labels: # $S: this line is enabled only when building source package +# $M: this line is enabled only when CONFIG_MODULES is enabled sed -e '/^DEL/d' -e 's/^\t*//' < Date: Sat, 30 Sep 2017 10:10:08 +0900 Subject: kbuild: rpm-pkg: replace $RPM_BUILD_ROOT with %{buildroot} $RPM_BUILD_ROOT must be escaped to prevent shell from expanding it when generating the spec file. %{build_root} is more readable than \$RPM_BUILD_ROOT. Signed-off-by: Masahiro Yamada --- scripts/package/mkspec | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'scripts') diff --git a/scripts/package/mkspec b/scripts/package/mkspec index d352a0188770..280027fad991 100755 --- a/scripts/package/mkspec +++ b/scripts/package/mkspec @@ -81,30 +81,30 @@ $S %build $S make %{?_smp_mflags} KBUILD_BUILD_VERSION=%{release} $S %install - mkdir -p \$RPM_BUILD_ROOT/boot + mkdir -p %{buildroot}/boot %ifarch ia64 - mkdir -p \$RPM_BUILD_ROOT/boot/efi - cp \$(make image_name) \$RPM_BUILD_ROOT/boot/efi/vmlinuz-$KERNELRELEASE - ln -s efi/vmlinuz-$KERNELRELEASE \$RPM_BUILD_ROOT/boot/ + mkdir -p %{buildroot}/boot/efi + cp \$(make image_name) %{buildroot}/boot/efi/vmlinuz-$KERNELRELEASE + ln -s efi/vmlinuz-$KERNELRELEASE %{buildroot}/boot/ %else - cp \$(make image_name) \$RPM_BUILD_ROOT/boot/vmlinuz-$KERNELRELEASE + cp \$(make image_name) %{buildroot}/boot/vmlinuz-$KERNELRELEASE %endif -$M make %{?_smp_mflags} INSTALL_MOD_PATH=\$RPM_BUILD_ROOT KBUILD_SRC= modules_install - make %{?_smp_mflags} INSTALL_HDR_PATH=\$RPM_BUILD_ROOT/usr KBUILD_SRC= headers_install - cp System.map \$RPM_BUILD_ROOT/boot/System.map-$KERNELRELEASE - cp .config \$RPM_BUILD_ROOT/boot/config-$KERNELRELEASE +$M make %{?_smp_mflags} INSTALL_MOD_PATH=%{buildroot} KBUILD_SRC= modules_install + make %{?_smp_mflags} INSTALL_HDR_PATH=%{buildroot}/usr KBUILD_SRC= headers_install + cp System.map %{buildroot}/boot/System.map-$KERNELRELEASE + cp .config %{buildroot}/boot/config-$KERNELRELEASE bzip2 -9 --keep vmlinux - mv vmlinux.bz2 \$RPM_BUILD_ROOT/boot/vmlinux-$KERNELRELEASE.bz2 -$S$M rm -f \$RPM_BUILD_ROOT/lib/modules/$KERNELRELEASE/build -$S$M rm -f \$RPM_BUILD_ROOT/lib/modules/$KERNELRELEASE/source -$S$M mkdir -p \$RPM_BUILD_ROOT/usr/src/kernels/$KERNELRELEASE -$S$M tar cf - . $EXCLUDES | tar xf - -C \$RPM_BUILD_ROOT/usr/src/kernels/$KERNELRELEASE -$S$M cd \$RPM_BUILD_ROOT/lib/modules/$KERNELRELEASE + mv vmlinux.bz2 %{buildroot}/boot/vmlinux-$KERNELRELEASE.bz2 +$S$M rm -f %{buildroot}/lib/modules/$KERNELRELEASE/build +$S$M rm -f %{buildroot}/lib/modules/$KERNELRELEASE/source +$S$M mkdir -p %{buildroot}/usr/src/kernels/$KERNELRELEASE +$S$M tar cf - . $EXCLUDES | tar xf - -C %{buildroot}/usr/src/kernels/$KERNELRELEASE +$S$M cd %{buildroot}/lib/modules/$KERNELRELEASE $S$M ln -sf /usr/src/kernels/$KERNELRELEASE build $S$M ln -sf /usr/src/kernels/$KERNELRELEASE source %clean - rm -rf \$RPM_BUILD_ROOT + rm -rf %{buildroot} %post if [ -x /sbin/installkernel -a -r /boot/vmlinuz-$KERNELRELEASE -a -r /boot/System.map-$KERNELRELEASE ]; then -- cgit v1.3-6-gb490 From 606625be47bc87b6fab0af10cd57aaa675cb9e42 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 30 Sep 2017 10:10:09 +0900 Subject: kbuild: rpm-pkg: fix jobserver unavailable warning If "make rpm-pkg" or "make binrpm-pkg" is run with -j[jobs] option, the following warning message is displayed. warning: jobserver unavailable: using -j1. Add '+' to parent make rule. Follow the suggestion. Signed-off-by: Masahiro Yamada --- scripts/package/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/package/Makefile b/scripts/package/Makefile index 73f9f3192b9f..b559671d28ca 100644 --- a/scripts/package/Makefile +++ b/scripts/package/Makefile @@ -50,7 +50,7 @@ rpm-pkg rpm: FORCE $(MAKE) clean $(CONFIG_SHELL) $(MKSPEC) >$(objtree)/kernel.spec $(call cmd,src_tar,$(KERNELPATH),kernel.spec) - rpmbuild $(RPMOPTS) --target $(UTS_MACHINE) -ta $(KERNELPATH).tar.gz + +rpmbuild $(RPMOPTS) --target $(UTS_MACHINE) -ta $(KERNELPATH).tar.gz rm $(KERNELPATH).tar.gz kernel.spec # binrpm-pkg @@ -58,7 +58,7 @@ rpm-pkg rpm: FORCE binrpm-pkg: FORCE $(MAKE) KBUILD_SRC= $(CONFIG_SHELL) $(MKSPEC) prebuilt > $(objtree)/binkernel.spec - rpmbuild $(RPMOPTS) --define "_builddir $(objtree)" --target \ + +rpmbuild $(RPMOPTS) --define "_builddir $(objtree)" --target \ $(UTS_MACHINE) -bb $(objtree)/binkernel.spec rm binkernel.spec -- cgit v1.3-6-gb490 From af60e207087975d069858741c44ed4f450330ac4 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 30 Sep 2017 10:10:10 +0900 Subject: kbuild: rpm-pkg: keep spec file until make mrproper If build fails during (bin)rpm-pkg, the spec file is not cleaned by anyone until the next successful build of the package. We do not have to immediately delete the spec file in case somebody may want to take a look at it. Instead, make them ignored by git, and cleaned up by make mrproper. Signed-off-by: Masahiro Yamada --- .gitignore | 5 +++++ scripts/package/Makefile | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/.gitignore b/.gitignore index 0c39aa20b6ba..4f034b853d7d 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,11 @@ Module.symvers /System.map /Module.markers +# +# RPM spec file (make rpm-pkg) +# +/*.spec + # # Debian directory (make deb-pkg) # diff --git a/scripts/package/Makefile b/scripts/package/Makefile index b559671d28ca..70eea1ed8c9c 100644 --- a/scripts/package/Makefile +++ b/scripts/package/Makefile @@ -51,7 +51,6 @@ rpm-pkg rpm: FORCE $(CONFIG_SHELL) $(MKSPEC) >$(objtree)/kernel.spec $(call cmd,src_tar,$(KERNELPATH),kernel.spec) +rpmbuild $(RPMOPTS) --target $(UTS_MACHINE) -ta $(KERNELPATH).tar.gz - rm $(KERNELPATH).tar.gz kernel.spec # binrpm-pkg # --------------------------------------------------------------------------- @@ -60,7 +59,8 @@ binrpm-pkg: FORCE $(CONFIG_SHELL) $(MKSPEC) prebuilt > $(objtree)/binkernel.spec +rpmbuild $(RPMOPTS) --define "_builddir $(objtree)" --target \ $(UTS_MACHINE) -bb $(objtree)/binkernel.spec - rm binkernel.spec + +clean-files += $(objtree)/*.spec # Deb target # --------------------------------------------------------------------------- -- cgit v1.3-6-gb490 From 8a16a070abaa61e95354755a320ca37cee544209 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sat, 30 Sep 2017 10:10:11 +0900 Subject: kbuild: rpm-pkg: do not force -jN in submake The spec file always passes %{?_smp_mflags}, but we have two problems here. [1] "make -jN rpm-pkg" emits the following warning message: make[2]: warning: -jN forced in submake: disabling jobserver mode. [2] We can not specify the number of jobs that run in parallel. Whether we give -jN or not from the top Makefile, the spec file always passes ${?_smp_mflags} to the build commands. ${?_smp_mflags} will be useful when we run rpmbuild by hand. When we invoke it from Makefile, -jN is propagated down to submake; it should not be overridden because we want to respect the number of jobs given by the user. Set _smp_mflags to empty string in this case. Signed-off-by: Masahiro Yamada --- scripts/package/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/package/Makefile b/scripts/package/Makefile index 70eea1ed8c9c..9ed96aefc72d 100644 --- a/scripts/package/Makefile +++ b/scripts/package/Makefile @@ -50,7 +50,8 @@ rpm-pkg rpm: FORCE $(MAKE) clean $(CONFIG_SHELL) $(MKSPEC) >$(objtree)/kernel.spec $(call cmd,src_tar,$(KERNELPATH),kernel.spec) - +rpmbuild $(RPMOPTS) --target $(UTS_MACHINE) -ta $(KERNELPATH).tar.gz + +rpmbuild $(RPMOPTS) --target $(UTS_MACHINE) -ta $(KERNELPATH).tar.gz \ + --define='_smp_mflags %{nil}' # binrpm-pkg # --------------------------------------------------------------------------- -- cgit v1.3-6-gb490 From bc27b77df1939b9567aa468c47d4a5784f40cfa1 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 23 Aug 2017 15:11:13 +0200 Subject: Coccinelle: setup_timer: improve messages from setup_timer Allow messages about multiple timers. Signed-off-by: Julia Lawall Signed-off-by: Masahiro Yamada --- scripts/coccinelle/api/setup_timer.cocci | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'scripts') diff --git a/scripts/coccinelle/api/setup_timer.cocci b/scripts/coccinelle/api/setup_timer.cocci index eb6bd9e4ab1a..b5ab0317fa03 100644 --- a/scripts/coccinelle/api/setup_timer.cocci +++ b/scripts/coccinelle/api/setup_timer.cocci @@ -104,11 +104,9 @@ position j0, j1, j2; ) @match_function_and_data_after_init_timer_context -depends on !patch && -!match_immediate_function_data_after_init_timer_context && - (context || org || report)@ +depends on !patch && (context || org || report)@ expression a, b, e1, e2, e3, e4, e5; -position j0, j1, j2; +position j0 != match_immediate_function_data_after_init_timer_context.j0,j1,j2; @@ * init_timer@j0 (&e1); @@ -124,13 +122,12 @@ position j0, j1, j2; * e1@j2.function = a; ) -@r3_context depends on !patch && -!match_immediate_function_data_after_init_timer_context && -!match_function_and_data_after_init_timer_context && - (context || org || report)@ +@r3_context depends on !patch && (context || org || report)@ expression c, e6, e7; position r1.p; -position j0, j1; +position j0 != + {match_immediate_function_data_after_init_timer_context.j0, + match_function_and_data_after_init_timer_context.j0}, j1; @@ * init_timer@j0@p (&e6); -- cgit v1.3-6-gb490 From 1b18d05c7c204a59e0ac66cbfa813a7173c4426e Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 20 Sep 2017 16:27:25 -0700 Subject: coccinelle: Improve setup_timer.cocci matching This improves the patch mode of setup_timer.cocci. Several patterns were missing: - assignments-before-init_timer() cases - limit the .data case removal to the specific struct timer_list instance - handling calls by dereference (timer->field vs timer.field) Cc: Gilles Muller Cc: Nicolas Palix Cc: Michal Marek Cc: cocci@systeme.lip6.fr Signed-off-by: Kees Cook Acked-by: Julia Lawall Signed-off-by: Masahiro Yamada --- scripts/coccinelle/api/setup_timer.cocci | 129 +++++++++++++++++++++++++------ 1 file changed, 105 insertions(+), 24 deletions(-) (limited to 'scripts') diff --git a/scripts/coccinelle/api/setup_timer.cocci b/scripts/coccinelle/api/setup_timer.cocci index b5ab0317fa03..e4577089dcb9 100644 --- a/scripts/coccinelle/api/setup_timer.cocci +++ b/scripts/coccinelle/api/setup_timer.cocci @@ -2,6 +2,7 @@ /// and data fields // Confidence: High // Copyright: (C) 2016 Vaishali Thakkar, Oracle. GPLv2 +// Copyright: (C) 2017 Kees Cook, Google. GPLv2 // Options: --no-includes --include-headers // Keywords: init_timer, setup_timer @@ -10,60 +11,123 @@ virtual context virtual org virtual report +// Match the common cases first to avoid Coccinelle parsing loops with +// "... when" clauses. + @match_immediate_function_data_after_init_timer depends on patch && !context && !org && !report@ expression e, func, da; @@ --init_timer (&e); -+setup_timer (&e, func, da); +-init_timer ++setup_timer + ( \(&e\|e\) ++, func, da + ); +( +-\(e.function\|e->function\) = func; +-\(e.data\|e->data\) = da; +| +-\(e.data\|e->data\) = da; +-\(e.function\|e->function\) = func; +) + +@match_immediate_function_data_before_init_timer +depends on patch && !context && !org && !report@ +expression e, func, da; +@@ +( +-\(e.function\|e->function\) = func; +-\(e.data\|e->data\) = da; +| +-\(e.data\|e->data\) = da; +-\(e.function\|e->function\) = func; +) +-init_timer ++setup_timer + ( \(&e\|e\) ++, func, da + ); + +@match_function_and_data_after_init_timer +depends on patch && !context && !org && !report@ +expression e, e2, e3, e4, e5, func, da; +@@ + +-init_timer ++setup_timer + ( \(&e\|e\) ++, func, da + ); + ... when != func = e2 + when != da = e3 ( -e.function = func; +... when != da = e4 -e.data = da; | +-e->function = func; +... when != da = e4 +-e->data = da; +| -e.data = da; +... when != func = e5 -e.function = func; +| +-e->data = da; +... when != func = e5 +-e->function = func; ) -@match_function_and_data_after_init_timer +@match_function_and_data_before_init_timer depends on patch && !context && !org && !report@ -expression e1, e2, e3, e4, e5, a, b; +expression e, e2, e3, e4, e5, func, da; @@ - --init_timer (&e1); -+setup_timer (&e1, a, b); - -... when != a = e2 - when != b = e3 ( --e1.function = a; -... when != b = e4 --e1.data = b; +-e.function = func; +... when != da = e4 +-e.data = da; | --e1.data = b; -... when != a = e5 --e1.function = a; +-e->function = func; +... when != da = e4 +-e->data = da; +| +-e.data = da; +... when != func = e5 +-e.function = func; +| +-e->data = da; +... when != func = e5 +-e->function = func; ) +... when != func = e2 + when != da = e3 +-init_timer ++setup_timer + ( \(&e\|e\) ++, func, da + ); @r1 exists@ +expression t; identifier f; position p; @@ f(...) { ... when any - init_timer@p(...) + init_timer@p(\(&t\|t\)) ... when any } @r2 exists@ +expression r1.t; identifier g != r1.f; -struct timer_list t; expression e8; @@ g(...) { ... when any - t.data = e8 + \(t.data\|t->data\) = e8 ... when any } @@ -77,14 +141,31 @@ p << r1.p; cocci.include_match(False) @r3 depends on patch && !context && !org && !report@ -expression e6, e7, c; +expression r1.t, func, e7; position r1.p; @@ --init_timer@p (&e6); -+setup_timer (&e6, c, 0UL); -... when != c = e7 --e6.function = c; +( +-init_timer@p(&t); ++setup_timer(&t, func, 0UL); +... when != func = e7 +-t.function = func; +| +-t.function = func; +... when != func = e7 +-init_timer@p(&t); ++setup_timer(&t, func, 0UL); +| +-init_timer@p(t); ++setup_timer(t, func, 0UL); +... when != func = e7 +-t->function = func; +| +-t->function = func; +... when != func = e7 +-init_timer@p(t); ++setup_timer(t, func, 0UL); +) // ---------------------------------------------------------------------------- -- cgit v1.3-6-gb490 From a44b86645a4a173a45e57d127ac037e88750ea6a Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 8 Oct 2017 21:18:41 +0200 Subject: coccinelle: api: detect identical chip data arrays This semantic patch detects duplicate arrays declared using BQ27XXX_DATA within a single structure. It is currently specific to the file drivers/power/supply/bq27xxx_battery.c. Nevertheless, having the script in the kernel will allow others to check their code if the data structures change in the future. Signed-off-by: Julia Lawall Signed-off-by: Masahiro Yamada --- scripts/coccinelle/api/check_bq27xxx_data.cocci | 161 ++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 scripts/coccinelle/api/check_bq27xxx_data.cocci (limited to 'scripts') diff --git a/scripts/coccinelle/api/check_bq27xxx_data.cocci b/scripts/coccinelle/api/check_bq27xxx_data.cocci new file mode 100644 index 000000000000..9212b85169d2 --- /dev/null +++ b/scripts/coccinelle/api/check_bq27xxx_data.cocci @@ -0,0 +1,161 @@ +/// Detect BQ27XXX_DATA structures with identical registers, dm registers or +/// properties. +//# Doesn't unfold macros used in register or property fields. +//# Requires OCaml scripting +/// +// Confidence: High +// Copyright: (C) 2017 Julia Lawall, Inria/LIP6, GPLv2. +// URL: http://coccinelle.lip6.fr/ +// Requires: 1.0.7 +// Keywords: BQ27XXX_DATA + +virtual report + +@initialize:ocaml@ +@@ + +let print_report p msg = + let p = List.hd p in + Printf.printf "%s:%d:%d-%d: %s" p.file p.line p.col p.col_end msg + +@str depends on report@ +type t; +identifier i,i1,i2; +expression e1,e2; +@@ + +t i[] = { + ..., + [e1] = BQ27XXX_DATA(i1,...), + ..., + [e2] = BQ27XXX_DATA(i2,...), + ..., +}; + +@script:ocaml tocheck@ +i1 << str.i1; +i2 << str.i2; +i1regs; i2regs; +i1dmregs; i2dmregs; +i1props; i2props; +@@ + +if not(i1 = i2) +then + begin + i1regs := make_ident (i1 ^ "_regs"); + i2regs := make_ident (i2 ^ "_regs"); + i1dmregs := make_ident (i1 ^ "_dm_regs"); + i2dmregs := make_ident (i2 ^ "_dm_regs"); + i1props := make_ident (i1 ^ "_props"); + i2props := make_ident (i2 ^ "_props") + end + +(* ---------------------------------------------------------------- *) + +@getregs1@ +typedef u8; +identifier tocheck.i1regs; +initializer list i1regs_vals; +position p1; +@@ + +u8 i1regs@p1[...] = { i1regs_vals, }; + +@getregs2@ +identifier tocheck.i2regs; +initializer list i2regs_vals; +position p2; +@@ + +u8 i2regs@p2[...] = { i2regs_vals, }; + +@script:ocaml@ +(_,i1regs_vals) << getregs1.i1regs_vals; +(_,i2regs_vals) << getregs2.i2regs_vals; +i1regs << tocheck.i1regs; +i2regs << tocheck.i2regs; +p1 << getregs1.p1; +p2 << getregs2.p2; +@@ + +if i1regs < i2regs && + List.sort compare i1regs_vals = List.sort compare i2regs_vals +then + let msg = + Printf.sprintf + "WARNING %s and %s (line %d) are identical\n" + i1regs i2regs (List.hd p2).line in + print_report p1 msg + +(* ---------------------------------------------------------------- *) + +@getdmregs1@ +identifier tocheck.i1dmregs; +initializer list i1dmregs_vals; +position p1; +@@ + +struct bq27xxx_dm_reg i1dmregs@p1[] = { i1dmregs_vals, }; + +@getdmregs2@ +identifier tocheck.i2dmregs; +initializer list i2dmregs_vals; +position p2; +@@ + +struct bq27xxx_dm_reg i2dmregs@p2[] = { i2dmregs_vals, }; + +@script:ocaml@ +(_,i1dmregs_vals) << getdmregs1.i1dmregs_vals; +(_,i2dmregs_vals) << getdmregs2.i2dmregs_vals; +i1dmregs << tocheck.i1dmregs; +i2dmregs << tocheck.i2dmregs; +p1 << getdmregs1.p1; +p2 << getdmregs2.p2; +@@ + +if i1dmregs < i2dmregs && + List.sort compare i1dmregs_vals = List.sort compare i2dmregs_vals +then + let msg = + Printf.sprintf + "WARNING %s and %s (line %d) are identical\n" + i1dmregs i2dmregs (List.hd p2).line in + print_report p1 msg + +(* ---------------------------------------------------------------- *) + +@getprops1@ +identifier tocheck.i1props; +initializer list[n1] i1props_vals; +position p1; +@@ + +enum power_supply_property i1props@p1[] = { i1props_vals, }; + +@getprops2@ +identifier tocheck.i2props; +initializer list[n2] i2props_vals; +position p2; +@@ + +enum power_supply_property i2props@p2[] = { i2props_vals, }; + +@script:ocaml@ +(_,i1props_vals) << getprops1.i1props_vals; +(_,i2props_vals) << getprops2.i2props_vals; +i1props << tocheck.i1props; +i2props << tocheck.i2props; +p1 << getprops1.p1; +p2 << getprops2.p2; +@@ + +if i1props < i2props && + List.sort compare i1props_vals = List.sort compare i2props_vals +then + let msg = + Printf.sprintf + "WARNING %s and %s (line %d) are identical\n" + i1props i2props (List.hd p2).line in + print_report p1 msg -- cgit v1.3-6-gb490 From 9ed07ada0e1476a676450056a20226b88076025e Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 15 Oct 2017 11:55:53 +0200 Subject: Coccinelle: make DEBUG_FILE option more useful Make coccicheck checked for the existence of DEBUG_FILE on each semantic patch, and bailed if it already existed. This meant that DEBUG_FILE was useless for checking more than one semantic patch at a time. Now the check is moved to the start of make coccicheck, and the 2> is changed to a 2>> to append to the file on each semantic patch. Furthermore, the spatch command that is run for each semantic patch is also added to the DEBUG_FILE, to make clear what each stdout trace corresponds to. Signed-off-by: Julia Lawall Signed-off-by: Masahiro Yamada --- scripts/coccicheck | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'scripts') diff --git a/scripts/coccicheck b/scripts/coccicheck index ec487b8e7051..864b17e05e63 100755 --- a/scripts/coccicheck +++ b/scripts/coccicheck @@ -122,15 +122,8 @@ run_cmd_parmap() { if [ $VERBOSE -ne 0 ] ; then echo "Running ($NPROC in parallel): $@" fi - if [ "$DEBUG_FILE" != "/dev/null" -a "$DEBUG_FILE" != "" ]; then - if [ -f $DEBUG_FILE ]; then - echo "Debug file $DEBUG_FILE exists, bailing" - exit - fi - else - DEBUG_FILE="/dev/null" - fi - $@ 2>$DEBUG_FILE + echo $@ >>$DEBUG_FILE + $@ 2>>$DEBUG_FILE if [[ $? -ne 0 ]]; then echo "coccicheck failed" exit $? @@ -246,6 +239,15 @@ coccinelle () { } +if [ "$DEBUG_FILE" != "/dev/null" -a "$DEBUG_FILE" != "" ]; then + if [ -f $DEBUG_FILE ]; then + echo "Debug file $DEBUG_FILE exists, bailing" + exit + fi +else + DEBUG_FILE="/dev/null" +fi + if [ "$COCCI" = "" ] ; then for f in `find $srctree/scripts/coccinelle/ -name '*.cocci' -type f | sort`; do coccinelle $f -- cgit v1.3-6-gb490 From e0be348e4d6ebd660c9558bcee50f648491cfef6 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 26 Oct 2017 13:50:38 +0900 Subject: coccinelle: grep Options and Requires fields more precisely Currently, the required version for badzero.cocci is picked up from its "Comments:" line since it contains the word "Requires". Surprisingly, ld-version.sh can extract the version number from the string "Requires Coccinelle version 1.0.0-rc20 or later", but this expectation is fragile. Fix the .cocci file. I removed "-rc20" because ld-version.sh cannot handle it. Make the coccicheck script to see exact patterns for "Options:" and "Requires:" in order to avoid accidental matching to what just happens to appear in comment lines. Signed-off-by: Masahiro Yamada Acked-by: Julia Lawall Acked-by: Nicolas Palix --- scripts/coccicheck | 4 ++-- scripts/coccinelle/null/badzero.cocci | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/coccicheck b/scripts/coccicheck index 864b17e05e63..97f28f0f9498 100755 --- a/scripts/coccicheck +++ b/scripts/coccicheck @@ -168,8 +168,8 @@ OPTIONS="$OPTIONS $SPFLAGS" coccinelle () { COCCI="$1" - OPT=`grep "Option" $COCCI | cut -d':' -f2` - REQ=`grep "Requires" $COCCI | cut -d':' -f2 | sed "s| ||"` + OPT=`grep "Options:" $COCCI | cut -d':' -f2` + REQ=`grep "Requires:" $COCCI | cut -d':' -f2 | sed "s| ||"` REQ_NUM=$(echo $REQ | ${DIR}/scripts/ld-version.sh) if [ "$REQ_NUM" != "0" ] ; then if [ "$SPATCH_VERSION_NUM" -lt "$REQ_NUM" ] ; then diff --git a/scripts/coccinelle/null/badzero.cocci b/scripts/coccinelle/null/badzero.cocci index 5551da2b4fe3..f597c8007b76 100644 --- a/scripts/coccinelle/null/badzero.cocci +++ b/scripts/coccinelle/null/badzero.cocci @@ -10,7 +10,7 @@ // Copyright: (C) 2012 Julia Lawall, INRIA/LIP6. GPLv2. // Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2. // URL: http://coccinelle.lip6.fr/ -// Comments: Requires Coccinelle version 1.0.0-rc20 or later +// Requires: 1.0.0 // Options: virtual patch -- cgit v1.3-6-gb490 From cd1af7cfbbdc7719b74ad9f3c88e50bb77713664 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 26 Oct 2017 13:55:51 +0900 Subject: coccinelle: fix verbose message about .cocci file being run If you run coccicheck with V=1 and COCCI=, you will see a strange path to the semantic patch file. For example, run the following: $ make V=1 COCCI=scripts/coccinelle/free/kfree.cocci coccicheck [ snip ] The semantic patch that makes this report is available in scriptcoccinelle/free/kfree.cocci. Notice "s/" was dropped from "scripts/coccinelle/free/kfree.cocci". When running coccicheck without O=, $srctree is expanded to ".", which represents one arbitrary character in the regular expression. Using sed is not a good choice here. Strip $srctree/ simply without sed. Signed-off-by: Masahiro Yamada Acked-by: Nicolas Palix --- scripts/coccicheck | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/coccicheck b/scripts/coccicheck index 97f28f0f9498..41a85b1ed35e 100755 --- a/scripts/coccicheck +++ b/scripts/coccicheck @@ -186,7 +186,7 @@ coccinelle () { if [ $VERBOSE -ne 0 -a $ONLINE -eq 0 ] ; then - FILE=`echo $COCCI | sed "s|$srctree/||"` + FILE=${COCCI#$srctree/} echo "Processing `basename $COCCI`" echo "with option(s) \"$OPT\"" -- cgit v1.3-6-gb490 From 69c4907ba1ee9e9428363c9419c9116bb28c402c Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 29 Oct 2017 01:43:09 +0200 Subject: Coccinelle: use false positive annotation /// is to describe the semantic patch, while //# indicates reasons for false positives. Signed-off-by: Julia Lawall Signed-off-by: Masahiro Yamada --- scripts/coccinelle/misc/ifcol.cocci | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/coccinelle/misc/ifcol.cocci b/scripts/coccinelle/misc/ifcol.cocci index d0d00ef1f12a..ffe75407c5d2 100644 --- a/scripts/coccinelle/misc/ifcol.cocci +++ b/scripts/coccinelle/misc/ifcol.cocci @@ -3,10 +3,10 @@ /// Sometimes, code after an if that is indented is actually intended to be /// part of the if branch. /// -/// This has a high rate of false positives, because Coccinelle's column -/// calculation does not distinguish between spaces and tabs, so code that -/// is not visually aligned may be considered to be in the same column. -/// +//# This has a high rate of false positives, because Coccinelle's column +//# calculation does not distinguish between spaces and tabs, so code that +//# is not visually aligned may be considered to be in the same column. +// // Confidence: Low // Copyright: (C) 2010 Nicolas Palix, DIKU. GPLv2. // Copyright: (C) 2010 Julia Lawall, DIKU. GPLv2. -- cgit v1.3-6-gb490 From bace64800bae72c563bab66f23c7221fddaae142 Mon Sep 17 00:00:00 2001 From: Sven Joachim Date: Thu, 9 Nov 2017 19:27:06 +0100 Subject: builddeb: Pass the kernel:debarch substvar to dpkg-genchanges At the end of "make bindeb-pkg" I noticed the following warning: dpkg-genchanges: warning: unknown substitution variable ${kernel:debarch} It turns out that since dpkg version 1.19.0 dpkg-genchanges honors substitution variables in the Description field, while earlier versions silently left them alone, see https://bugs.debian.org/856547. The result is an incomplete description of the linux-headers package in the generated .changes file. Fix it by passing the kernel:debarch substitution variable to dpkg-genchanges. Signed-off-by: Sven Joachim Signed-off-by: Masahiro Yamada --- scripts/package/builddeb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/package/builddeb b/scripts/package/builddeb index 0bc87473f68f..b4f0f2b3f8d2 100755 --- a/scripts/package/builddeb +++ b/scripts/package/builddeb @@ -408,9 +408,9 @@ EOF dpkg-source -cdebian/control -ldebian/changelog --format="3.0 (custom)" --target-format="3.0 (quilt)" \ -b / ../${sourcename}_${version}.orig.tar.gz ../${sourcename}_${packageversion}.debian.tar.gz mv ${sourcename}_${packageversion}*dsc .. - dpkg-genchanges > ../${sourcename}_${packageversion}_${debarch}.changes + dpkg-genchanges -Vkernel:debarch="${debarch}" > ../${sourcename}_${packageversion}_${debarch}.changes else - dpkg-genchanges -b > ../${sourcename}_${packageversion}_${debarch}.changes + dpkg-genchanges -b -Vkernel:debarch="${debarch}" > ../${sourcename}_${packageversion}_${debarch}.changes fi exit 0 -- cgit v1.3-6-gb490 From 6851ba1a1b22ba2e0800002d531bf04ced22ec18 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 12 Nov 2017 16:02:18 +0100 Subject: coccinelle: use exists to improve efficiency This just needs to find any reassignment of the loop iterator, and doesn't need such a thing on all execution paths, so use exists on the first rule. Signed-off-by: Julia Lawall Signed-off-by: Masahiro Yamada --- scripts/coccinelle/iterators/list_entry_update.cocci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/coccinelle/iterators/list_entry_update.cocci b/scripts/coccinelle/iterators/list_entry_update.cocci index 873f444e7137..be6f9f1abb34 100644 --- a/scripts/coccinelle/iterators/list_entry_update.cocci +++ b/scripts/coccinelle/iterators/list_entry_update.cocci @@ -15,7 +15,7 @@ virtual context virtual org virtual report -@r@ +@r exists@ iterator name list_for_each_entry; expression x,E; position p1,p2; -- cgit v1.3-6-gb490 From 937c812dfc0a25343d56b07734438610a1fb7b46 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 13 Nov 2017 06:53:35 +0100 Subject: coccinelle: orplus: reorganize to improve performance Adding two #define constants is less common than performing & and | operations on them, so put the addition first to reduce the set of cases that have to be considered in detail. At the same time, add & and | patterns for both arguments of +, to account for commutativity and obtain more results. Running time is divided by 3 when applying this to the whole kernel on my laptop with an Intel i5-6200U CPU. Signed-off-by: Julia Lawall Signed-off-by: Masahiro Yamada --- scripts/coccinelle/misc/orplus.cocci | 43 ++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 14 deletions(-) (limited to 'scripts') diff --git a/scripts/coccinelle/misc/orplus.cocci b/scripts/coccinelle/misc/orplus.cocci index 81fabf379390..08de5be73693 100644 --- a/scripts/coccinelle/misc/orplus.cocci +++ b/scripts/coccinelle/misc/orplus.cocci @@ -14,7 +14,19 @@ virtual report virtual context @r@ -constant c; +constant c,c1; +identifier i,i1; +position p; +@@ + +( + c1 + c - 1 +| + c1@i1 +@p c@i +) + +@s@ +constant r.c, r.c1; identifier i; expression e; @@ @@ -27,28 +39,31 @@ e & c@i e |= c@i | e &= c@i +| +e | c1@i +| +e & c1@i +| +e |= c1@i +| +e &= c1@i ) -@s@ -constant r.c,c1; -identifier i1; -position p; +@depends on s@ +position r.p; +constant c1,c2; @@ -( - c1 + c - 1 -| -*c1@i1 +@p c -) +* c1 +@p c2 -@script:python depends on org@ -p << s.p; +@script:python depends on s && org@ +p << r.p; @@ cocci.print_main("sum of probable bitmasks, consider |",p) -@script:python depends on report@ -p << s.p; +@script:python depends on s && report@ +p << r.p; @@ msg = "WARNING: sum of probable bitmasks, consider |" -- cgit v1.3-6-gb490 From 9a234a2e384349f21afac8d718aa294a668ad4fa Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 13 Nov 2017 19:29:34 +0900 Subject: kbuild: create directory for make cache only when necessary Currently, the existence of $(dir $(make-cache)) is always checked, and created if it is missing. We can avoid unnecessary system calls by some tricks. [1] If KBUILD_SRC is unset, we are building in the source tree. The output directory checks can be entirely skipped. [2] If at least one cache data is found, it means the cache file was included. Obviously its directory exists. Skip "mkdir -p". [3] If Makefile does not contain any call of __run-and-store, it will not create a cache file. No need to create its directory. [4] The "mkdir -p" should be only invoked by the first call of __run-and-store Signed-off-by: Masahiro Yamada Reviewed-by: Douglas Anderson --- scripts/Kbuild.include | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/Kbuild.include b/scripts/Kbuild.include index be1c9d65eaf4..065324a8046f 100644 --- a/scripts/Kbuild.include +++ b/scripts/Kbuild.include @@ -99,18 +99,19 @@ cc-cross-prefix = \ # Include values from last time make-cache := $(if $(KBUILD_EXTMOD),$(KBUILD_EXTMOD)/,$(if $(obj),$(obj)/)).cache.mk -ifeq ($(wildcard $(dir $(make-cache))),) -$(shell mkdir -p '$(dir $(make-cache))') -endif $(make-cache): ; -include $(make-cache) +cached-data := $(filter __cached_%, $(.VARIABLES)) + # If cache exceeds 1000 lines, shrink it down to 500. -ifneq ($(word 1000,$(filter __cached_%, $(.VARIABLES))),) +ifneq ($(word 1000,$(cached-data)),) $(shell tail -n 500 $(make-cache) > $(make-cache).tmp; \ mv $(make-cache).tmp $(make-cache)) endif +create-cache-dir := $(if $(KBUILD_SRC),$(if $(cache-data),,1)) + # Usage: $(call __sanitize-opt,Hello=Hola$(comma)Goodbye Adios) # # Convert all '$', ')', '(', '\', '=', ' ', ',', ':' to '_' @@ -136,6 +137,10 @@ __sanitize-opt = $(subst $$,_,$(subst $(right_paren),_,$(subst $(left_paren),_,$ define __run-and-store ifeq ($(origin $(1)),undefined) $$(eval $(1) := $$(shell $$(2))) +ifeq ($(create-cache-dir),1) + $$(shell mkdir -p $(dir $(make-cache))) + $$(eval create-cache-dir :=) +endif $$(shell echo '$(1) := $$($(1))' >> $(make-cache)) endif endef -- cgit v1.3-6-gb490 From 2982c953570b2bced858613d70443c2c6a90587b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 13 Nov 2017 19:29:35 +0900 Subject: kbuild: remove redundant $(wildcard ...) for cmd_files calculation I do not see any reason why $(wildcard ...) needs to be called twice for computing cmd_files. Remove the first one. Signed-off-by: Masahiro Yamada --- Makefile | 3 +-- scripts/Makefile.build | 3 +-- scripts/Makefile.headersinst | 3 +-- scripts/Makefile.modpost | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) (limited to 'scripts') diff --git a/Makefile b/Makefile index a7476e6934f1..58dd24520c9e 100644 --- a/Makefile +++ b/Makefile @@ -1693,8 +1693,7 @@ cmd_crmodverdir = $(Q)mkdir -p $(MODVERDIR) \ # read all saved command lines -targets := $(wildcard $(sort $(targets))) -cmd_files := $(wildcard .*.cmd $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd)) +cmd_files := $(wildcard .*.cmd $(foreach f,$(sort $(targets)),$(dir $(f)).$(notdir $(f)).cmd)) ifneq ($(cmd_files),) $(cmd_files): ; # Do not try to update included dependency files diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 061d0c3a420a..62d5314c3b71 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -583,8 +583,7 @@ FORCE: # optimization, we don't need to read them if the target does not # exist, we will rebuild anyway in that case. -targets := $(wildcard $(sort $(targets))) -cmd_files := $(wildcard $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd)) +cmd_files := $(wildcard $(foreach f,$(sort $(targets)),$(dir $(f)).$(notdir $(f)).cmd)) ifneq ($(cmd_files),) include $(cmd_files) diff --git a/scripts/Makefile.headersinst b/scripts/Makefile.headersinst index 5692d7a66163..c6fb2b75d813 100644 --- a/scripts/Makefile.headersinst +++ b/scripts/Makefile.headersinst @@ -114,9 +114,8 @@ $(check-file): scripts/headers_check.pl $(output-files) FORCE endif -targets := $(wildcard $(sort $(targets))) cmd_files := $(wildcard \ - $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd)) + $(foreach f,$(sort $(targets)),$(dir $(f)).$(notdir $(f)).cmd)) ifneq ($(cmd_files),) include $(cmd_files) diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost index 16923ba4b5b1..cf125c11ca41 100644 --- a/scripts/Makefile.modpost +++ b/scripts/Makefile.modpost @@ -143,8 +143,7 @@ FORCE: # optimization, we don't need to read them if the target does not # exist, we will rebuild anyway in that case. -targets := $(wildcard $(sort $(targets))) -cmd_files := $(wildcard $(foreach f,$(targets),$(dir $(f)).$(notdir $(f)).cmd)) +cmd_files := $(wildcard $(foreach f,$(sort $(targets)),$(dir $(f)).$(notdir $(f)).cmd)) ifneq ($(cmd_files),) include $(cmd_files) -- cgit v1.3-6-gb490 From 591f66899784ae0afa13ff9a3eb5ce0a4358e48b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 13 Nov 2017 19:29:36 +0900 Subject: kbuild: filter-out PHONY targets from "targets" The variable "targets" contains object paths for which existing .*.cmd files should be included. scripts/Makefile.build automatically adds $(MAKECMDGOALS) to "targets" as follows: targets += $(extra-y) $(MAKECMDGOALS) $(always) The $(MAKECMDGOALS) is a PHONY target in several places. PHONY targets never create .*.cmd files. Signed-off-by: Masahiro Yamada --- scripts/Makefile.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 62d5314c3b71..6f603770b08e 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -562,7 +562,7 @@ $(multi-used-m): FORCE $(call multi_depend, $(multi-used-m), .o, -objs -y -m) targets += $(multi-used-y) $(multi-used-m) - +targets := $(filter-out $(PHONY), $(targets)) # Descending # --------------------------------------------------------------------------- -- cgit v1.3-6-gb490 From 8a78756eb545a6fb8007fa154a626ca2bc208027 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 13 Nov 2017 19:29:37 +0900 Subject: kbuild: create object directories simpler and faster For the out-of-tree build, scripts/Makefile.build creates output directories, but this operation is not efficient. scripts/Makefile.lib calculates obj-dirs as follows: obj-dirs := $(dir $(multi-objs) $(obj-y)) Please notice $(sort ...) is not used here. Usually the result is as many "./" as objects here. For a lot of duplicated paths, the following command is invoked. _dummy := $(foreach d,$(obj-dirs), $(shell [ -d $(d) ] || mkdir -p $(d))) Then, the costly shell command is run over and over again. I see many points for optimization: [1] Use $(sort ...) to cut down duplicated paths before passing them to system call [2] Use single $(shell ...) instead of repeating it with $(foreach ...) This will reduce forking. [3] We can calculate obj-dirs more simply. Most of objects are already accumulated in $(targets). So, $(dir $(targets)) is fine and more comprehensive. I also removed ugly code in arch/x86/entry/vdso/Makefile. This is now really unnecessary. Signed-off-by: Masahiro Yamada Acked-by: Ingo Molnar Tested-by: Douglas Anderson --- arch/x86/entry/vdso/Makefile | 4 ---- scripts/Makefile.build | 15 ++++++--------- scripts/Makefile.host | 12 ------------ scripts/Makefile.lib | 5 ----- 4 files changed, 6 insertions(+), 30 deletions(-) (limited to 'scripts') diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile index d5409660f5de..f8e3d85256ad 100644 --- a/arch/x86/entry/vdso/Makefile +++ b/arch/x86/entry/vdso/Makefile @@ -129,10 +129,6 @@ $(obj)/vdsox32.so.dbg: $(src)/vdsox32.lds $(vobjx32s) FORCE CPPFLAGS_vdso32.lds = $(CPPFLAGS_vdso.lds) VDSO_LDFLAGS_vdso32.lds = -m32 -Wl,-m,elf_i386 -Wl,-soname=linux-gate.so.1 -# This makes sure the $(obj) subdirectory exists even though vdso32/ -# is not a kbuild sub-make subdirectory. -override obj-dirs = $(dir $(obj)) $(obj)/vdso32/ - targets += vdso32/vdso32.lds targets += vdso32/note.o vdso32/system_call.o vdso32/sigreturn.o targets += vdso32/vclock_gettime.o diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 6f603770b08e..496ecd825c71 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -64,15 +64,6 @@ ifneq ($(hostprogs-y)$(hostprogs-m)$(hostlibs-y)$(hostlibs-m)$(hostcxxlibs-y)$(h include scripts/Makefile.host endif -ifneq ($(KBUILD_SRC),) -# Create output directory if not already present -_dummy := $(shell [ -d $(obj) ] || mkdir -p $(obj)) - -# Create directories for object files if directory does not exist -# Needed when obj-y := dir/file.o syntax is used -_dummy := $(foreach d,$(obj-dirs), $(shell [ -d $(d) ] || mkdir -p $(d))) -endif - ifndef obj $(warning kbuild: Makefile.build is included improperly) endif @@ -589,6 +580,12 @@ ifneq ($(cmd_files),) include $(cmd_files) endif +ifneq ($(KBUILD_SRC),) +# Create directories for object files if they do not exist +obj-dirs := $(sort $(obj) $(patsubst %/,%, $(dir $(targets)))) +$(shell mkdir -p $(obj-dirs)) +endif + # Declare the contents of the .PHONY variable as phony. We keep that # information in a variable se we can use it in if_changed and friends. diff --git a/scripts/Makefile.host b/scripts/Makefile.host index 9cfd5c84d76f..a5e03838eca8 100644 --- a/scripts/Makefile.host +++ b/scripts/Makefile.host @@ -48,15 +48,6 @@ host-cxxobjs := $(sort $(foreach m,$(host-cxxmulti),$($(m)-cxxobjs))) host-cshobjs := $(sort $(foreach m,$(host-cshlib),$($(m:.so=-objs)))) host-cxxshobjs := $(sort $(foreach m,$(host-cxxshlib),$($(m:.so=-objs)))) -# output directory for programs/.o files -# hostprogs-y := tools/build may have been specified. -# Retrieve also directory of .o files from prog-objs or prog-cxxobjs notation -host-objdirs := $(dir $(__hostprogs) $(host-cobjs) $(host-cxxobjs)) - -host-objdirs := $(strip $(sort $(filter-out ./,$(host-objdirs)))) - - -__hostprogs := $(addprefix $(obj)/,$(__hostprogs)) host-csingle := $(addprefix $(obj)/,$(host-csingle)) host-cmulti := $(addprefix $(obj)/,$(host-cmulti)) host-cobjs := $(addprefix $(obj)/,$(host-cobjs)) @@ -66,9 +57,6 @@ host-cshlib := $(addprefix $(obj)/,$(host-cshlib)) host-cxxshlib := $(addprefix $(obj)/,$(host-cxxshlib)) host-cshobjs := $(addprefix $(obj)/,$(host-cshobjs)) host-cxxshobjs := $(addprefix $(obj)/,$(host-cxxshobjs)) -host-objdirs := $(addprefix $(obj)/,$(host-objdirs)) - -obj-dirs += $(host-objdirs) ##### # Handle options to gcc. Support building with separate output directory diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index 4d88ad70fd96..5fbc46daa0f8 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -50,15 +50,11 @@ single-used-m := $(sort $(filter-out $(multi-used-m),$(obj-m))) # objects depend on those (obviously) multi-objs-y := $(foreach m, $(multi-used-y), $($(m:.o=-objs)) $($(m:.o=-y))) multi-objs-m := $(foreach m, $(multi-used-m), $($(m:.o=-objs)) $($(m:.o=-y))) -multi-objs := $(multi-objs-y) $(multi-objs-m) # $(subdir-obj-y) is the list of objects in $(obj-y) which uses dir/ to # tell kbuild to descend subdir-obj-y := $(filter %/built-in.o, $(obj-y)) -# $(obj-dirs) is a list of directories that contain object files -obj-dirs := $(dir $(multi-objs) $(obj-y)) - # Replace multi-part objects by their individual parts, look at local dir only real-objs-y := $(foreach m, $(filter-out $(subdir-obj-y), $(obj-y)), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y))),$($(m:.o=-objs)) $($(m:.o=-y)),$(m))) $(extra-y) real-objs-m := $(foreach m, $(obj-m), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y)) $($(m:.o=-m))),$($(m:.o=-objs)) $($(m:.o=-y)) $($(m:.o=-m)),$(m))) @@ -81,7 +77,6 @@ multi-used-m := $(addprefix $(obj)/,$(multi-used-m)) multi-objs-y := $(addprefix $(obj)/,$(multi-objs-y)) multi-objs-m := $(addprefix $(obj)/,$(multi-objs-m)) subdir-ym := $(addprefix $(obj)/,$(subdir-ym)) -obj-dirs := $(addprefix $(obj)/,$(obj-dirs)) # These flags are needed for modversions and compiling, so we define them here # $(modname_flags) defines KBUILD_MODNAME as the name of the module it will -- cgit v1.3-6-gb490 From c4da7ed0e7d715b159b11efd10408510ffed1aa3 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 13 Nov 2017 19:29:38 +0900 Subject: kbuild: optimize object directory creation for incremental build The previous commit largely optimized the object directory creation. We can optimize it more for incremental build. There are already *.cmd files in the output directory. The existing *.cmd files have been picked up by $(wildcard ...). Obviously, directories containing them exist too, so we can skip "mkdir -p". With this, Kbuild runs almost zero "mkdir -p" in incremental building. Signed-off-by: Masahiro Yamada --- scripts/Makefile.build | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'scripts') diff --git a/scripts/Makefile.build b/scripts/Makefile.build index 496ecd825c71..8624924a2991 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -583,8 +583,13 @@ endif ifneq ($(KBUILD_SRC),) # Create directories for object files if they do not exist obj-dirs := $(sort $(obj) $(patsubst %/,%, $(dir $(targets)))) +# If cmd_files exist, their directories apparently exist. Skip mkdir. +exist-dirs := $(sort $(patsubst %/,%, $(dir $(cmd_files)))) +obj-dirs := $(strip $(filter-out $(exist-dirs), $(obj-dirs))) +ifneq ($(obj-dirs),) $(shell mkdir -p $(obj-dirs)) endif +endif # Declare the contents of the .PHONY variable as phony. We keep that # information in a variable se we can use it in if_changed and friends. -- cgit v1.3-6-gb490 From e474ed45777bc230648186c0db990bd290383ada Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 13 Nov 2017 19:33:19 +0900 Subject: kbuild: specify FORCE in Makefile.headersinst as .PHONY target Swap the order of ".PHONY: $(PHONY)" and "PHONY += FORCE" so that FORCE is correctly specified as a .PHONY target. Use a preferred way for specifying $(subdirs) as .PHONY targets. Signed-off-by: Masahiro Yamada --- scripts/Makefile.headersinst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/Makefile.headersinst b/scripts/Makefile.headersinst index c6fb2b75d813..086a821ba8f5 100644 --- a/scripts/Makefile.headersinst +++ b/scripts/Makefile.headersinst @@ -26,7 +26,7 @@ subdirs := $(patsubst $(srcdir)/%/,%,\ # Recursion __headers: $(subdirs) -.PHONY: $(subdirs) +PHONY += $(subdirs) $(subdirs): $(Q)$(MAKE) $(hdr-inst)=$(obj)/$@ dst=$(dst)/$@ @@ -123,6 +123,7 @@ endif endif # skip-inst -.PHONY: $(PHONY) PHONY += FORCE FORCE: ; + +.PHONY: $(PHONY) -- cgit v1.3-6-gb490 From 2f3b55ac0e15857f46a3696e964bb244305223bf Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 13 Nov 2017 19:40:40 +0900 Subject: selinux: remove unnecessary assignment to subdir- Makefile.clean descends into $(subdir-y). Dummy assignment to subdir- is meaningless. Signed-off-by: Masahiro Yamada Acked-by: Paul Moore --- scripts/selinux/Makefile | 1 - 1 file changed, 1 deletion(-) (limited to 'scripts') diff --git a/scripts/selinux/Makefile b/scripts/selinux/Makefile index e8049da1831f..b3048b894a39 100644 --- a/scripts/selinux/Makefile +++ b/scripts/selinux/Makefile @@ -1,2 +1 @@ subdir-y := mdp genheaders -subdir- += mdp genheaders -- cgit v1.3-6-gb490 From 192efb7a1f9b69ce2ec1212ee8c24fb9b4a80a35 Mon Sep 17 00:00:00 2001 From: Maninder Singh Date: Wed, 15 Nov 2017 17:31:14 -0800 Subject: bloat-o-meter: provide 3 different arguments for data, function and All This patch provides 3 new arguments for bloat-o-meter 1) -c -> for all (showing function and data differently) 2) -d -> data 3) -t -> function output: ./scripts/bloat-o-meter -c "file1" "file2" add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-152 (-152) Function old new delta main 412 260 -152 Total: Before=548, After=396, chg -27.74% ########################################################## add/remove: 1/0 grow/shrink: 1/0 up/down: 84/0 (84) Data old new delta arr - 64 +64 backtrace 60 80 +20 Total: Before=109, After=193, chg +77.06% ########################################################## add/remove: 0/1 grow/shrink: 0/0 up/down: 0/-64 (-64) RO Data old new delta arr 64 - -64 Total: Before=68, After=4, chg -94.12% [maninder1.s@samsung.com: v1 -> v2] Link: http://lkml.kernel.org/r/1506569402-24787-1-git-send-email-maninder1.s@samsung.com Link: http://lkml.kernel.org/r/1506336313-27187-1-git-send-email-maninder1.s@samsung.com Signed-off-by: Vaneet Narang Signed-off-by: Maninder Singh Cc: Amit Sahrawat Cc: Andi Kleen Cc: Michal Marek Cc: Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/bloat-o-meter | 89 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 34 deletions(-) (limited to 'scripts') diff --git a/scripts/bloat-o-meter b/scripts/bloat-o-meter index a27677146410..6f099f915dcf 100755 --- a/scripts/bloat-o-meter +++ b/scripts/bloat-o-meter @@ -12,18 +12,22 @@ from signal import signal, SIGPIPE, SIG_DFL signal(SIGPIPE, SIG_DFL) -if len(sys.argv) != 3: - sys.stderr.write("usage: %s file1 file2\n" % sys.argv[0]) +if len(sys.argv) < 3: + sys.stderr.write("usage: %s [option] file1 file2\n" % sys.argv[0]) + sys.stderr.write("The options are:\n") + sys.stderr.write("-c cateogrize output based on symbole type\n") + sys.stderr.write("-d Show delta of Data Section\n") + sys.stderr.write("-t Show delta of text Section\n") sys.exit(-1) re_NUMBER = re.compile(r'\.[0-9]+') -def getsizes(file): +def getsizes(file, format): sym = {} with os.popen("nm --size-sort " + file) as f: for line in f: size, type, name = line.split() - if type in "tTdDbBrR": + if type in format: # strip generated symbols if name.startswith("__mod_"): continue if name.startswith("SyS_"): continue @@ -34,44 +38,61 @@ def getsizes(file): sym[name] = sym.get(name, 0) + int(size, 16) return sym -old = getsizes(sys.argv[1]) -new = getsizes(sys.argv[2]) -grow, shrink, add, remove, up, down = 0, 0, 0, 0, 0, 0 -delta, common = [], {} -otot, ntot = 0, 0 +def calc(oldfile, newfile, format): + old = getsizes(oldfile, format) + new = getsizes(newfile, format) + grow, shrink, add, remove, up, down = 0, 0, 0, 0, 0, 0 + delta, common = [], {} + otot, ntot = 0, 0 -for a in old: - if a in new: - common[a] = 1 + for a in old: + if a in new: + common[a] = 1 -for name in old: - otot += old[name] - if name not in common: - remove += 1 - down += old[name] - delta.append((-old[name], name)) + for name in old: + otot += old[name] + if name not in common: + remove += 1 + down += old[name] + delta.append((-old[name], name)) -for name in new: - ntot += new[name] - if name not in common: - add += 1 - up += new[name] - delta.append((new[name], name)) + for name in new: + ntot += new[name] + if name not in common: + add += 1 + up += new[name] + delta.append((new[name], name)) -for name in common: + for name in common: d = new.get(name, 0) - old.get(name, 0) if d>0: grow, up = grow+1, up+d if d<0: shrink, down = shrink+1, down-d delta.append((d, name)) -delta.sort() -delta.reverse() + delta.sort() + delta.reverse() + return grow, shrink, add, remove, up, down, delta, old, new, otot, ntot -print("add/remove: %s/%s grow/shrink: %s/%s up/down: %s/%s (%s)" % \ - (add, remove, grow, shrink, up, -down, up-down)) -print("%-40s %7s %7s %+7s" % ("function", "old", "new", "delta")) -for d, n in delta: - if d: print("%-40s %7s %7s %+7d" % (n, old.get(n,"-"), new.get(n,"-"), d)) +def print_result(symboltype, symbolformat, argc): + grow, shrink, add, remove, up, down, delta, old, new, otot, ntot = \ + calc(sys.argv[argc - 1], sys.argv[argc], symbolformat) -print("Total: Before=%d, After=%d, chg %+.2f%%" % \ - (otot, ntot, (ntot - otot)*100.0/otot)) + print("add/remove: %s/%s grow/shrink: %s/%s up/down: %s/%s (%s)" % \ + (add, remove, grow, shrink, up, -down, up-down)) + print("%-40s %7s %7s %+7s" % (symboltype, "old", "new", "delta")) + for d, n in delta: + if d: print("%-40s %7s %7s %+7d" % (n, old.get(n,"-"), new.get(n,"-"), d)) + + print("Total: Before=%d, After=%d, chg %+.2f%%" % \ + (otot, ntot, (ntot - otot)*100.0/otot)) + +if sys.argv[1] == "-c": + print_result("Function", "tT", 3) + print_result("Data", "dDbB", 3) + print_result("RO Data", "rR", 3) +elif sys.argv[1] == "-d": + print_result("Data", "dDbBrR", 3) +elif sys.argv[1] == "-t": + print_result("Function", "tT", 3) +else: + print_result("Function", "tTdDbBrR", 2) -- cgit v1.3-6-gb490 From 4675ff05de2d76d167336b368bd07f3fef6ed5a6 Mon Sep 17 00:00:00 2001 From: "Levin, Alexander (Sasha Levin)" Date: Wed, 15 Nov 2017 17:36:02 -0800 Subject: kmemcheck: rip it out Fix up makefiles, remove references, and git rm kmemcheck. Link: http://lkml.kernel.org/r/20171007030159.22241-4-alexander.levin@verizon.com Signed-off-by: Sasha Levin Cc: Steven Rostedt Cc: Vegard Nossum Cc: Pekka Enberg Cc: Michal Hocko Cc: Eric W. Biederman Cc: Alexander Potapenko Cc: Tim Hansen Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/admin-guide/kernel-parameters.txt | 7 - Documentation/dev-tools/index.rst | 1 - Documentation/dev-tools/kmemcheck.rst | 733 ------------------------ MAINTAINERS | 10 - arch/x86/Kconfig | 3 +- arch/x86/include/asm/kmemcheck.h | 42 -- arch/x86/include/asm/string_32.h | 9 - arch/x86/include/asm/string_64.h | 8 - arch/x86/kernel/cpu/intel.c | 15 - arch/x86/mm/Makefile | 2 - arch/x86/mm/init.c | 5 +- arch/x86/mm/kmemcheck/Makefile | 1 - arch/x86/mm/kmemcheck/error.c | 227 -------- arch/x86/mm/kmemcheck/error.h | 15 - arch/x86/mm/kmemcheck/kmemcheck.c | 658 --------------------- arch/x86/mm/kmemcheck/opcode.c | 106 ---- arch/x86/mm/kmemcheck/opcode.h | 9 - arch/x86/mm/kmemcheck/pte.c | 22 - arch/x86/mm/kmemcheck/pte.h | 10 - arch/x86/mm/kmemcheck/selftest.c | 70 --- arch/x86/mm/kmemcheck/selftest.h | 6 - arch/x86/mm/kmemcheck/shadow.c | 173 ------ arch/x86/mm/kmemcheck/shadow.h | 18 - include/linux/interrupt.h | 15 - include/linux/kmemcheck.h | 171 ------ kernel/softirq.c | 10 - kernel/sysctl.c | 10 - lib/Kconfig.debug | 6 +- lib/Kconfig.kmemcheck | 94 --- mm/Kconfig.debug | 1 - mm/Makefile | 2 - mm/kmemcheck.c | 125 ---- mm/slub.c | 5 +- scripts/kernel-doc | 2 - tools/include/linux/kmemcheck.h | 8 - 35 files changed, 7 insertions(+), 2592 deletions(-) delete mode 100644 Documentation/dev-tools/kmemcheck.rst delete mode 100644 arch/x86/mm/kmemcheck/Makefile delete mode 100644 arch/x86/mm/kmemcheck/kmemcheck.c delete mode 100644 arch/x86/mm/kmemcheck/shadow.c delete mode 100644 lib/Kconfig.kmemcheck (limited to 'scripts') diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index b74e13312fdc..00bb04972612 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1864,13 +1864,6 @@ Built with CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y, the default is off. - kmemcheck= [X86] Boot-time kmemcheck enable/disable/one-shot mode - Valid arguments: 0, 1, 2 - kmemcheck=0 (disabled) - kmemcheck=1 (enabled) - kmemcheck=2 (one-shot mode) - Default: 2 (one-shot mode) - kvm.ignore_msrs=[KVM] Ignore guest accesses to unhandled MSRs. Default is 0 (don't ignore, but inject #GP) diff --git a/Documentation/dev-tools/index.rst b/Documentation/dev-tools/index.rst index a81787cd47d7..e313925fb0fa 100644 --- a/Documentation/dev-tools/index.rst +++ b/Documentation/dev-tools/index.rst @@ -21,7 +21,6 @@ whole; patches welcome! kasan ubsan kmemleak - kmemcheck gdb-kernel-debugging kgdb kselftest diff --git a/Documentation/dev-tools/kmemcheck.rst b/Documentation/dev-tools/kmemcheck.rst deleted file mode 100644 index 7f3d1985de74..000000000000 --- a/Documentation/dev-tools/kmemcheck.rst +++ /dev/null @@ -1,733 +0,0 @@ -Getting started with kmemcheck -============================== - -Vegard Nossum - - -Introduction ------------- - -kmemcheck is a debugging feature for the Linux Kernel. More specifically, it -is a dynamic checker that detects and warns about some uses of uninitialized -memory. - -Userspace programmers might be familiar with Valgrind's memcheck. The main -difference between memcheck and kmemcheck is that memcheck works for userspace -programs only, and kmemcheck works for the kernel only. The implementations -are of course vastly different. Because of this, kmemcheck is not as accurate -as memcheck, but it turns out to be good enough in practice to discover real -programmer errors that the compiler is not able to find through static -analysis. - -Enabling kmemcheck on a kernel will probably slow it down to the extent that -the machine will not be usable for normal workloads such as e.g. an -interactive desktop. kmemcheck will also cause the kernel to use about twice -as much memory as normal. For this reason, kmemcheck is strictly a debugging -feature. - - -Downloading ------------ - -As of version 2.6.31-rc1, kmemcheck is included in the mainline kernel. - - -Configuring and compiling -------------------------- - -kmemcheck only works for the x86 (both 32- and 64-bit) platform. A number of -configuration variables must have specific settings in order for the kmemcheck -menu to even appear in "menuconfig". These are: - -- ``CONFIG_CC_OPTIMIZE_FOR_SIZE=n`` - This option is located under "General setup" / "Optimize for size". - - Without this, gcc will use certain optimizations that usually lead to - false positive warnings from kmemcheck. An example of this is a 16-bit - field in a struct, where gcc may load 32 bits, then discard the upper - 16 bits. kmemcheck sees only the 32-bit load, and may trigger a - warning for the upper 16 bits (if they're uninitialized). - -- ``CONFIG_SLAB=y`` or ``CONFIG_SLUB=y`` - This option is located under "General setup" / "Choose SLAB - allocator". - -- ``CONFIG_FUNCTION_TRACER=n`` - This option is located under "Kernel hacking" / "Tracers" / "Kernel - Function Tracer" - - When function tracing is compiled in, gcc emits a call to another - function at the beginning of every function. This means that when the - page fault handler is called, the ftrace framework will be called - before kmemcheck has had a chance to handle the fault. If ftrace then - modifies memory that was tracked by kmemcheck, the result is an - endless recursive page fault. - -- ``CONFIG_DEBUG_PAGEALLOC=n`` - This option is located under "Kernel hacking" / "Memory Debugging" - / "Debug page memory allocations". - -In addition, I highly recommend turning on ``CONFIG_DEBUG_INFO=y``. This is also -located under "Kernel hacking". With this, you will be able to get line number -information from the kmemcheck warnings, which is extremely valuable in -debugging a problem. This option is not mandatory, however, because it slows -down the compilation process and produces a much bigger kernel image. - -Now the kmemcheck menu should be visible (under "Kernel hacking" / "Memory -Debugging" / "kmemcheck: trap use of uninitialized memory"). Here follows -a description of the kmemcheck configuration variables: - -- ``CONFIG_KMEMCHECK`` - This must be enabled in order to use kmemcheck at all... - -- ``CONFIG_KMEMCHECK_``[``DISABLED`` | ``ENABLED`` | ``ONESHOT``]``_BY_DEFAULT`` - This option controls the status of kmemcheck at boot-time. "Enabled" - will enable kmemcheck right from the start, "disabled" will boot the - kernel as normal (but with the kmemcheck code compiled in, so it can - be enabled at run-time after the kernel has booted), and "one-shot" is - a special mode which will turn kmemcheck off automatically after - detecting the first use of uninitialized memory. - - If you are using kmemcheck to actively debug a problem, then you - probably want to choose "enabled" here. - - The one-shot mode is mostly useful in automated test setups because it - can prevent floods of warnings and increase the chances of the machine - surviving in case something is really wrong. In other cases, the one- - shot mode could actually be counter-productive because it would turn - itself off at the very first error -- in the case of a false positive - too -- and this would come in the way of debugging the specific - problem you were interested in. - - If you would like to use your kernel as normal, but with a chance to - enable kmemcheck in case of some problem, it might be a good idea to - choose "disabled" here. When kmemcheck is disabled, most of the run- - time overhead is not incurred, and the kernel will be almost as fast - as normal. - -- ``CONFIG_KMEMCHECK_QUEUE_SIZE`` - Select the maximum number of error reports to store in an internal - (fixed-size) buffer. Since errors can occur virtually anywhere and in - any context, we need a temporary storage area which is guaranteed not - to generate any other page faults when accessed. The queue will be - emptied as soon as a tasklet may be scheduled. If the queue is full, - new error reports will be lost. - - The default value of 64 is probably fine. If some code produces more - than 64 errors within an irqs-off section, then the code is likely to - produce many, many more, too, and these additional reports seldom give - any more information (the first report is usually the most valuable - anyway). - - This number might have to be adjusted if you are not using serial - console or similar to capture the kernel log. If you are using the - "dmesg" command to save the log, then getting a lot of kmemcheck - warnings might overflow the kernel log itself, and the earlier reports - will get lost in that way instead. Try setting this to 10 or so on - such a setup. - -- ``CONFIG_KMEMCHECK_SHADOW_COPY_SHIFT`` - Select the number of shadow bytes to save along with each entry of the - error-report queue. These bytes indicate what parts of an allocation - are initialized, uninitialized, etc. and will be displayed when an - error is detected to help the debugging of a particular problem. - - The number entered here is actually the logarithm of the number of - bytes that will be saved. So if you pick for example 5 here, kmemcheck - will save 2^5 = 32 bytes. - - The default value should be fine for debugging most problems. It also - fits nicely within 80 columns. - -- ``CONFIG_KMEMCHECK_PARTIAL_OK`` - This option (when enabled) works around certain GCC optimizations that - produce 32-bit reads from 16-bit variables where the upper 16 bits are - thrown away afterwards. - - The default value (enabled) is recommended. This may of course hide - some real errors, but disabling it would probably produce a lot of - false positives. - -- ``CONFIG_KMEMCHECK_BITOPS_OK`` - This option silences warnings that would be generated for bit-field - accesses where not all the bits are initialized at the same time. This - may also hide some real bugs. - - This option is probably obsolete, or it should be replaced with - the kmemcheck-/bitfield-annotations for the code in question. The - default value is therefore fine. - -Now compile the kernel as usual. - - -How to use ----------- - -Booting -~~~~~~~ - -First some information about the command-line options. There is only one -option specific to kmemcheck, and this is called "kmemcheck". It can be used -to override the default mode as chosen by the ``CONFIG_KMEMCHECK_*_BY_DEFAULT`` -option. Its possible settings are: - -- ``kmemcheck=0`` (disabled) -- ``kmemcheck=1`` (enabled) -- ``kmemcheck=2`` (one-shot mode) - -If SLUB debugging has been enabled in the kernel, it may take precedence over -kmemcheck in such a way that the slab caches which are under SLUB debugging -will not be tracked by kmemcheck. In order to ensure that this doesn't happen -(even though it shouldn't by default), use SLUB's boot option ``slub_debug``, -like this: ``slub_debug=-`` - -In fact, this option may also be used for fine-grained control over SLUB vs. -kmemcheck. For example, if the command line includes -``kmemcheck=1 slub_debug=,dentry``, then SLUB debugging will be used only -for the "dentry" slab cache, and with kmemcheck tracking all the other -caches. This is advanced usage, however, and is not generally recommended. - - -Run-time enable/disable -~~~~~~~~~~~~~~~~~~~~~~~ - -When the kernel has booted, it is possible to enable or disable kmemcheck at -run-time. WARNING: This feature is still experimental and may cause false -positive warnings to appear. Therefore, try not to use this. If you find that -it doesn't work properly (e.g. you see an unreasonable amount of warnings), I -will be happy to take bug reports. - -Use the file ``/proc/sys/kernel/kmemcheck`` for this purpose, e.g.:: - - $ echo 0 > /proc/sys/kernel/kmemcheck # disables kmemcheck - -The numbers are the same as for the ``kmemcheck=`` command-line option. - - -Debugging -~~~~~~~~~ - -A typical report will look something like this:: - - WARNING: kmemcheck: Caught 32-bit read from uninitialized memory (ffff88003e4a2024) - 80000000000000000000000000000000000000000088ffff0000000000000000 - i i i i u u u u i i i i i i i i u u u u u u u u u u u u u u u u - ^ - - Pid: 1856, comm: ntpdate Not tainted 2.6.29-rc5 #264 945P-A - RIP: 0010:[] [] __dequeue_signal+0xc8/0x190 - RSP: 0018:ffff88003cdf7d98 EFLAGS: 00210002 - RAX: 0000000000000030 RBX: ffff88003d4ea968 RCX: 0000000000000009 - RDX: ffff88003e5d6018 RSI: ffff88003e5d6024 RDI: ffff88003cdf7e84 - RBP: ffff88003cdf7db8 R08: ffff88003e5d6000 R09: 0000000000000000 - R10: 0000000000000080 R11: 0000000000000000 R12: 000000000000000e - R13: ffff88003cdf7e78 R14: ffff88003d530710 R15: ffff88003d5a98c8 - FS: 0000000000000000(0000) GS:ffff880001982000(0063) knlGS:00000 - CS: 0010 DS: 002b ES: 002b CR0: 0000000080050033 - CR2: ffff88003f806ea0 CR3: 000000003c036000 CR4: 00000000000006a0 - DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 - DR3: 0000000000000000 DR6: 00000000ffff4ff0 DR7: 0000000000000400 - [] dequeue_signal+0x8e/0x170 - [] get_signal_to_deliver+0x98/0x390 - [] do_notify_resume+0xad/0x7d0 - [] int_signal+0x12/0x17 - [] 0xffffffffffffffff - -The single most valuable information in this report is the RIP (or EIP on 32- -bit) value. This will help us pinpoint exactly which instruction that caused -the warning. - -If your kernel was compiled with ``CONFIG_DEBUG_INFO=y``, then all we have to do -is give this address to the addr2line program, like this:: - - $ addr2line -e vmlinux -i ffffffff8104ede8 - arch/x86/include/asm/string_64.h:12 - include/asm-generic/siginfo.h:287 - kernel/signal.c:380 - kernel/signal.c:410 - -The "``-e vmlinux``" tells addr2line which file to look in. **IMPORTANT:** -This must be the vmlinux of the kernel that produced the warning in the -first place! If not, the line number information will almost certainly be -wrong. - -The "``-i``" tells addr2line to also print the line numbers of inlined -functions. In this case, the flag was very important, because otherwise, -it would only have printed the first line, which is just a call to -``memcpy()``, which could be called from a thousand places in the kernel, and -is therefore not very useful. These inlined functions would not show up in -the stack trace above, simply because the kernel doesn't load the extra -debugging information. This technique can of course be used with ordinary -kernel oopses as well. - -In this case, it's the caller of ``memcpy()`` that is interesting, and it can be -found in ``include/asm-generic/siginfo.h``, line 287:: - - 281 static inline void copy_siginfo(struct siginfo *to, struct siginfo *from) - 282 { - 283 if (from->si_code < 0) - 284 memcpy(to, from, sizeof(*to)); - 285 else - 286 /* _sigchld is currently the largest know union member */ - 287 memcpy(to, from, __ARCH_SI_PREAMBLE_SIZE + sizeof(from->_sifields._sigchld)); - 288 } - -Since this was a read (kmemcheck usually warns about reads only, though it can -warn about writes to unallocated or freed memory as well), it was probably the -"from" argument which contained some uninitialized bytes. Following the chain -of calls, we move upwards to see where "from" was allocated or initialized, -``kernel/signal.c``, line 380:: - - 359 static void collect_signal(int sig, struct sigpending *list, siginfo_t *info) - 360 { - ... - 367 list_for_each_entry(q, &list->list, list) { - 368 if (q->info.si_signo == sig) { - 369 if (first) - 370 goto still_pending; - 371 first = q; - ... - 377 if (first) { - 378 still_pending: - 379 list_del_init(&first->list); - 380 copy_siginfo(info, &first->info); - 381 __sigqueue_free(first); - ... - 392 } - 393 } - -Here, it is ``&first->info`` that is being passed on to ``copy_siginfo()``. The -variable ``first`` was found on a list -- passed in as the second argument to -``collect_signal()``. We continue our journey through the stack, to figure out -where the item on "list" was allocated or initialized. We move to line 410:: - - 395 static int __dequeue_signal(struct sigpending *pending, sigset_t *mask, - 396 siginfo_t *info) - 397 { - ... - 410 collect_signal(sig, pending, info); - ... - 414 } - -Now we need to follow the ``pending`` pointer, since that is being passed on to -``collect_signal()`` as ``list``. At this point, we've run out of lines from the -"addr2line" output. Not to worry, we just paste the next addresses from the -kmemcheck stack dump, i.e.:: - - [] dequeue_signal+0x8e/0x170 - [] get_signal_to_deliver+0x98/0x390 - [] do_notify_resume+0xad/0x7d0 - [] int_signal+0x12/0x17 - - $ addr2line -e vmlinux -i ffffffff8104f04e ffffffff81050bd8 \ - ffffffff8100b87d ffffffff8100c7b5 - kernel/signal.c:446 - kernel/signal.c:1806 - arch/x86/kernel/signal.c:805 - arch/x86/kernel/signal.c:871 - arch/x86/kernel/entry_64.S:694 - -Remember that since these addresses were found on the stack and not as the -RIP value, they actually point to the _next_ instruction (they are return -addresses). This becomes obvious when we look at the code for line 446:: - - 422 int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info) - 423 { - ... - 431 signr = __dequeue_signal(&tsk->signal->shared_pending, - 432 mask, info); - 433 /* - 434 * itimer signal ? - 435 * - 436 * itimers are process shared and we restart periodic - 437 * itimers in the signal delivery path to prevent DoS - 438 * attacks in the high resolution timer case. This is - 439 * compliant with the old way of self restarting - 440 * itimers, as the SIGALRM is a legacy signal and only - 441 * queued once. Changing the restart behaviour to - 442 * restart the timer in the signal dequeue path is - 443 * reducing the timer noise on heavy loaded !highres - 444 * systems too. - 445 */ - 446 if (unlikely(signr == SIGALRM)) { - ... - 489 } - -So instead of looking at 446, we should be looking at 431, which is the line -that executes just before 446. Here we see that what we are looking for is -``&tsk->signal->shared_pending``. - -Our next task is now to figure out which function that puts items on this -``shared_pending`` list. A crude, but efficient tool, is ``git grep``:: - - $ git grep -n 'shared_pending' kernel/ - ... - kernel/signal.c:828: pending = group ? &t->signal->shared_pending : &t->pending; - kernel/signal.c:1339: pending = group ? &t->signal->shared_pending : &t->pending; - ... - -There were more results, but none of them were related to list operations, -and these were the only assignments. We inspect the line numbers more closely -and find that this is indeed where items are being added to the list:: - - 816 static int send_signal(int sig, struct siginfo *info, struct task_struct *t, - 817 int group) - 818 { - ... - 828 pending = group ? &t->signal->shared_pending : &t->pending; - ... - 851 q = __sigqueue_alloc(t, GFP_ATOMIC, (sig < SIGRTMIN && - 852 (is_si_special(info) || - 853 info->si_code >= 0))); - 854 if (q) { - 855 list_add_tail(&q->list, &pending->list); - ... - 890 } - -and:: - - 1309 int send_sigqueue(struct sigqueue *q, struct task_struct *t, int group) - 1310 { - .... - 1339 pending = group ? &t->signal->shared_pending : &t->pending; - 1340 list_add_tail(&q->list, &pending->list); - .... - 1347 } - -In the first case, the list element we are looking for, ``q``, is being -returned from the function ``__sigqueue_alloc()``, which looks like an -allocation function. Let's take a look at it:: - - 187 static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags, - 188 int override_rlimit) - 189 { - 190 struct sigqueue *q = NULL; - 191 struct user_struct *user; - 192 - 193 /* - 194 * We won't get problems with the target's UID changing under us - 195 * because changing it requires RCU be used, and if t != current, the - 196 * caller must be holding the RCU readlock (by way of a spinlock) and - 197 * we use RCU protection here - 198 */ - 199 user = get_uid(__task_cred(t)->user); - 200 atomic_inc(&user->sigpending); - 201 if (override_rlimit || - 202 atomic_read(&user->sigpending) <= - 203 t->signal->rlim[RLIMIT_SIGPENDING].rlim_cur) - 204 q = kmem_cache_alloc(sigqueue_cachep, flags); - 205 if (unlikely(q == NULL)) { - 206 atomic_dec(&user->sigpending); - 207 free_uid(user); - 208 } else { - 209 INIT_LIST_HEAD(&q->list); - 210 q->flags = 0; - 211 q->user = user; - 212 } - 213 - 214 return q; - 215 } - -We see that this function initializes ``q->list``, ``q->flags``, and -``q->user``. It seems that now is the time to look at the definition of -``struct sigqueue``, e.g.:: - - 14 struct sigqueue { - 15 struct list_head list; - 16 int flags; - 17 siginfo_t info; - 18 struct user_struct *user; - 19 }; - -And, you might remember, it was a ``memcpy()`` on ``&first->info`` that -caused the warning, so this makes perfect sense. It also seems reasonable -to assume that it is the caller of ``__sigqueue_alloc()`` that has the -responsibility of filling out (initializing) this member. - -But just which fields of the struct were uninitialized? Let's look at -kmemcheck's report again:: - - WARNING: kmemcheck: Caught 32-bit read from uninitialized memory (ffff88003e4a2024) - 80000000000000000000000000000000000000000088ffff0000000000000000 - i i i i u u u u i i i i i i i i u u u u u u u u u u u u u u u u - ^ - -These first two lines are the memory dump of the memory object itself, and -the shadow bytemap, respectively. The memory object itself is in this case -``&first->info``. Just beware that the start of this dump is NOT the start -of the object itself! The position of the caret (^) corresponds with the -address of the read (ffff88003e4a2024). - -The shadow bytemap dump legend is as follows: - -- i: initialized -- u: uninitialized -- a: unallocated (memory has been allocated by the slab layer, but has not - yet been handed off to anybody) -- f: freed (memory has been allocated by the slab layer, but has been freed - by the previous owner) - -In order to figure out where (relative to the start of the object) the -uninitialized memory was located, we have to look at the disassembly. For -that, we'll need the RIP address again:: - - RIP: 0010:[] [] __dequeue_signal+0xc8/0x190 - - $ objdump -d --no-show-raw-insn vmlinux | grep -C 8 ffffffff8104ede8: - ffffffff8104edc8: mov %r8,0x8(%r8) - ffffffff8104edcc: test %r10d,%r10d - ffffffff8104edcf: js ffffffff8104ee88 <__dequeue_signal+0x168> - ffffffff8104edd5: mov %rax,%rdx - ffffffff8104edd8: mov $0xc,%ecx - ffffffff8104eddd: mov %r13,%rdi - ffffffff8104ede0: mov $0x30,%eax - ffffffff8104ede5: mov %rdx,%rsi - ffffffff8104ede8: rep movsl %ds:(%rsi),%es:(%rdi) - ffffffff8104edea: test $0x2,%al - ffffffff8104edec: je ffffffff8104edf0 <__dequeue_signal+0xd0> - ffffffff8104edee: movsw %ds:(%rsi),%es:(%rdi) - ffffffff8104edf0: test $0x1,%al - ffffffff8104edf2: je ffffffff8104edf5 <__dequeue_signal+0xd5> - ffffffff8104edf4: movsb %ds:(%rsi),%es:(%rdi) - ffffffff8104edf5: mov %r8,%rdi - ffffffff8104edf8: callq ffffffff8104de60 <__sigqueue_free> - -As expected, it's the "``rep movsl``" instruction from the ``memcpy()`` -that causes the warning. We know about ``REP MOVSL`` that it uses the register -``RCX`` to count the number of remaining iterations. By taking a look at the -register dump again (from the kmemcheck report), we can figure out how many -bytes were left to copy:: - - RAX: 0000000000000030 RBX: ffff88003d4ea968 RCX: 0000000000000009 - -By looking at the disassembly, we also see that ``%ecx`` is being loaded -with the value ``$0xc`` just before (ffffffff8104edd8), so we are very -lucky. Keep in mind that this is the number of iterations, not bytes. And -since this is a "long" operation, we need to multiply by 4 to get the -number of bytes. So this means that the uninitialized value was encountered -at 4 * (0xc - 0x9) = 12 bytes from the start of the object. - -We can now try to figure out which field of the "``struct siginfo``" that -was not initialized. This is the beginning of the struct:: - - 40 typedef struct siginfo { - 41 int si_signo; - 42 int si_errno; - 43 int si_code; - 44 - 45 union { - .. - 92 } _sifields; - 93 } siginfo_t; - -On 64-bit, the int is 4 bytes long, so it must the union member that has -not been initialized. We can verify this using gdb:: - - $ gdb vmlinux - ... - (gdb) p &((struct siginfo *) 0)->_sifields - $1 = (union {...} *) 0x10 - -Actually, it seems that the union member is located at offset 0x10 -- which -means that gcc has inserted 4 bytes of padding between the members ``si_code`` -and ``_sifields``. We can now get a fuller picture of the memory dump:: - - _----------------------------=> si_code - / _--------------------=> (padding) - | / _------------=> _sifields(._kill._pid) - | | / _----=> _sifields(._kill._uid) - | | | / - -------|-------|-------|-------| - 80000000000000000000000000000000000000000088ffff0000000000000000 - i i i i u u u u i i i i i i i i u u u u u u u u u u u u u u u u - -This allows us to realize another important fact: ``si_code`` contains the -value 0x80. Remember that x86 is little endian, so the first 4 bytes -"80000000" are really the number 0x00000080. With a bit of research, we -find that this is actually the constant ``SI_KERNEL`` defined in -``include/asm-generic/siginfo.h``:: - - 144 #define SI_KERNEL 0x80 /* sent by the kernel from somewhere */ - -This macro is used in exactly one place in the x86 kernel: In ``send_signal()`` -in ``kernel/signal.c``:: - - 816 static int send_signal(int sig, struct siginfo *info, struct task_struct *t, - 817 int group) - 818 { - ... - 828 pending = group ? &t->signal->shared_pending : &t->pending; - ... - 851 q = __sigqueue_alloc(t, GFP_ATOMIC, (sig < SIGRTMIN && - 852 (is_si_special(info) || - 853 info->si_code >= 0))); - 854 if (q) { - 855 list_add_tail(&q->list, &pending->list); - 856 switch ((unsigned long) info) { - ... - 865 case (unsigned long) SEND_SIG_PRIV: - 866 q->info.si_signo = sig; - 867 q->info.si_errno = 0; - 868 q->info.si_code = SI_KERNEL; - 869 q->info.si_pid = 0; - 870 q->info.si_uid = 0; - 871 break; - ... - 890 } - -Not only does this match with the ``.si_code`` member, it also matches the place -we found earlier when looking for where siginfo_t objects are enqueued on the -``shared_pending`` list. - -So to sum up: It seems that it is the padding introduced by the compiler -between two struct fields that is uninitialized, and this gets reported when -we do a ``memcpy()`` on the struct. This means that we have identified a false -positive warning. - -Normally, kmemcheck will not report uninitialized accesses in ``memcpy()`` calls -when both the source and destination addresses are tracked. (Instead, we copy -the shadow bytemap as well). In this case, the destination address clearly -was not tracked. We can dig a little deeper into the stack trace from above:: - - arch/x86/kernel/signal.c:805 - arch/x86/kernel/signal.c:871 - arch/x86/kernel/entry_64.S:694 - -And we clearly see that the destination siginfo object is located on the -stack:: - - 782 static void do_signal(struct pt_regs *regs) - 783 { - 784 struct k_sigaction ka; - 785 siginfo_t info; - ... - 804 signr = get_signal_to_deliver(&info, &ka, regs, NULL); - ... - 854 } - -And this ``&info`` is what eventually gets passed to ``copy_siginfo()`` as the -destination argument. - -Now, even though we didn't find an actual error here, the example is still a -good one, because it shows how one would go about to find out what the report -was all about. - - -Annotating false positives -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -There are a few different ways to make annotations in the source code that -will keep kmemcheck from checking and reporting certain allocations. Here -they are: - -- ``__GFP_NOTRACK_FALSE_POSITIVE`` - This flag can be passed to ``kmalloc()`` or ``kmem_cache_alloc()`` - (therefore also to other functions that end up calling one of - these) to indicate that the allocation should not be tracked - because it would lead to a false positive report. This is a "big - hammer" way of silencing kmemcheck; after all, even if the false - positive pertains to particular field in a struct, for example, we - will now lose the ability to find (real) errors in other parts of - the same struct. - - Example:: - - /* No warnings will ever trigger on accessing any part of x */ - x = kmalloc(sizeof *x, GFP_KERNEL | __GFP_NOTRACK_FALSE_POSITIVE); - -- ``kmemcheck_bitfield_begin(name)``/``kmemcheck_bitfield_end(name)`` and - ``kmemcheck_annotate_bitfield(ptr, name)`` - The first two of these three macros can be used inside struct - definitions to signal, respectively, the beginning and end of a - bitfield. Additionally, this will assign the bitfield a name, which - is given as an argument to the macros. - - Having used these markers, one can later use - kmemcheck_annotate_bitfield() at the point of allocation, to indicate - which parts of the allocation is part of a bitfield. - - Example:: - - struct foo { - int x; - - kmemcheck_bitfield_begin(flags); - int flag_a:1; - int flag_b:1; - kmemcheck_bitfield_end(flags); - - int y; - }; - - struct foo *x = kmalloc(sizeof *x); - - /* No warnings will trigger on accessing the bitfield of x */ - kmemcheck_annotate_bitfield(x, flags); - - Note that ``kmemcheck_annotate_bitfield()`` can be used even before the - return value of ``kmalloc()`` is checked -- in other words, passing NULL - as the first argument is legal (and will do nothing). - - -Reporting errors ----------------- - -As we have seen, kmemcheck will produce false positive reports. Therefore, it -is not very wise to blindly post kmemcheck warnings to mailing lists and -maintainers. Instead, I encourage maintainers and developers to find errors -in their own code. If you get a warning, you can try to work around it, try -to figure out if it's a real error or not, or simply ignore it. Most -developers know their own code and will quickly and efficiently determine the -root cause of a kmemcheck report. This is therefore also the most efficient -way to work with kmemcheck. - -That said, we (the kmemcheck maintainers) will always be on the lookout for -false positives that we can annotate and silence. So whatever you find, -please drop us a note privately! Kernel configs and steps to reproduce (if -available) are of course a great help too. - -Happy hacking! - - -Technical description ---------------------- - -kmemcheck works by marking memory pages non-present. This means that whenever -somebody attempts to access the page, a page fault is generated. The page -fault handler notices that the page was in fact only hidden, and so it calls -on the kmemcheck code to make further investigations. - -When the investigations are completed, kmemcheck "shows" the page by marking -it present (as it would be under normal circumstances). This way, the -interrupted code can continue as usual. - -But after the instruction has been executed, we should hide the page again, so -that we can catch the next access too! Now kmemcheck makes use of a debugging -feature of the processor, namely single-stepping. When the processor has -finished the one instruction that generated the memory access, a debug -exception is raised. From here, we simply hide the page again and continue -execution, this time with the single-stepping feature turned off. - -kmemcheck requires some assistance from the memory allocator in order to work. -The memory allocator needs to - - 1. Tell kmemcheck about newly allocated pages and pages that are about to - be freed. This allows kmemcheck to set up and tear down the shadow memory - for the pages in question. The shadow memory stores the status of each - byte in the allocation proper, e.g. whether it is initialized or - uninitialized. - - 2. Tell kmemcheck which parts of memory should be marked uninitialized. - There are actually a few more states, such as "not yet allocated" and - "recently freed". - -If a slab cache is set up using the SLAB_NOTRACK flag, it will never return -memory that can take page faults because of kmemcheck. - -If a slab cache is NOT set up using the SLAB_NOTRACK flag, callers can still -request memory with the __GFP_NOTRACK or __GFP_NOTRACK_FALSE_POSITIVE flags. -This does not prevent the page faults from occurring, however, but marks the -object in question as being initialized so that no warnings will ever be -produced for this object. - -Currently, the SLAB and SLUB allocators are supported by kmemcheck. diff --git a/MAINTAINERS b/MAINTAINERS index 7e9c887ad951..ac814d3dd1c1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7688,16 +7688,6 @@ F: include/linux/kdb.h F: include/linux/kgdb.h F: kernel/debug/ -KMEMCHECK -M: Vegard Nossum -M: Pekka Enberg -S: Maintained -F: Documentation/dev-tools/kmemcheck.rst -F: arch/x86/include/asm/kmemcheck.h -F: arch/x86/mm/kmemcheck/ -F: include/linux/kmemcheck.h -F: mm/kmemcheck.c - KMEMLEAK M: Catalin Marinas S: Maintained diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index f08977d82ca0..cb678192da4a 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -112,7 +112,6 @@ config X86 select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_KASAN if X86_64 && SPARSEMEM_VMEMMAP select HAVE_ARCH_KGDB - select HAVE_ARCH_KMEMCHECK select HAVE_ARCH_MMAP_RND_BITS if MMU select HAVE_ARCH_MMAP_RND_COMPAT_BITS if MMU && COMPAT select HAVE_ARCH_COMPAT_MMAP_BASES if MMU && COMPAT @@ -1430,7 +1429,7 @@ config ARCH_DMA_ADDR_T_64BIT config X86_DIRECT_GBPAGES def_bool y - depends on X86_64 && !DEBUG_PAGEALLOC && !KMEMCHECK + depends on X86_64 && !DEBUG_PAGEALLOC ---help--- Certain kernel features effectively disable kernel linear 1 GB mappings (even if the CPU otherwise diff --git a/arch/x86/include/asm/kmemcheck.h b/arch/x86/include/asm/kmemcheck.h index 945a0337fbcf..ea32a7d3cf1b 100644 --- a/arch/x86/include/asm/kmemcheck.h +++ b/arch/x86/include/asm/kmemcheck.h @@ -1,43 +1 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#ifndef ASM_X86_KMEMCHECK_H -#define ASM_X86_KMEMCHECK_H - -#include -#include - -#ifdef CONFIG_KMEMCHECK -bool kmemcheck_active(struct pt_regs *regs); - -void kmemcheck_show(struct pt_regs *regs); -void kmemcheck_hide(struct pt_regs *regs); - -bool kmemcheck_fault(struct pt_regs *regs, - unsigned long address, unsigned long error_code); -bool kmemcheck_trap(struct pt_regs *regs); -#else -static inline bool kmemcheck_active(struct pt_regs *regs) -{ - return false; -} - -static inline void kmemcheck_show(struct pt_regs *regs) -{ -} - -static inline void kmemcheck_hide(struct pt_regs *regs) -{ -} - -static inline bool kmemcheck_fault(struct pt_regs *regs, - unsigned long address, unsigned long error_code) -{ - return false; -} - -static inline bool kmemcheck_trap(struct pt_regs *regs) -{ - return false; -} -#endif /* CONFIG_KMEMCHECK */ - -#endif diff --git a/arch/x86/include/asm/string_32.h b/arch/x86/include/asm/string_32.h index 076502241eae..55d392c6bd29 100644 --- a/arch/x86/include/asm/string_32.h +++ b/arch/x86/include/asm/string_32.h @@ -179,8 +179,6 @@ static inline void *__memcpy3d(void *to, const void *from, size_t len) * No 3D Now! */ -#ifndef CONFIG_KMEMCHECK - #if (__GNUC__ >= 4) #define memcpy(t, f, n) __builtin_memcpy(t, f, n) #else @@ -189,13 +187,6 @@ static inline void *__memcpy3d(void *to, const void *from, size_t len) ? __constant_memcpy((t), (f), (n)) \ : __memcpy((t), (f), (n))) #endif -#else -/* - * kmemcheck becomes very happy if we use the REP instructions unconditionally, - * because it means that we know both memory operands in advance. - */ -#define memcpy(t, f, n) __memcpy((t), (f), (n)) -#endif #endif #endif /* !CONFIG_FORTIFY_SOURCE */ diff --git a/arch/x86/include/asm/string_64.h b/arch/x86/include/asm/string_64.h index 0b1b4445f4c5..533f74c300c2 100644 --- a/arch/x86/include/asm/string_64.h +++ b/arch/x86/include/asm/string_64.h @@ -33,7 +33,6 @@ extern void *memcpy(void *to, const void *from, size_t len); extern void *__memcpy(void *to, const void *from, size_t len); #ifndef CONFIG_FORTIFY_SOURCE -#ifndef CONFIG_KMEMCHECK #if (__GNUC__ == 4 && __GNUC_MINOR__ < 3) || __GNUC__ < 4 #define memcpy(dst, src, len) \ ({ \ @@ -46,13 +45,6 @@ extern void *__memcpy(void *to, const void *from, size_t len); __ret; \ }) #endif -#else -/* - * kmemcheck becomes very happy if we use the REP instructions unconditionally, - * because it means that we know both memory operands in advance. - */ -#define memcpy(dst, src, len) __inline_memcpy((dst), (src), (len)) -#endif #endif /* !CONFIG_FORTIFY_SOURCE */ #define __HAVE_ARCH_MEMSET diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c index b720dacac051..b1af22073e28 100644 --- a/arch/x86/kernel/cpu/intel.c +++ b/arch/x86/kernel/cpu/intel.c @@ -187,21 +187,6 @@ static void early_init_intel(struct cpuinfo_x86 *c) if (c->x86 == 6 && c->x86_model < 15) clear_cpu_cap(c, X86_FEATURE_PAT); -#ifdef CONFIG_KMEMCHECK - /* - * P4s have a "fast strings" feature which causes single- - * stepping REP instructions to only generate a #DB on - * cache-line boundaries. - * - * Ingo Molnar reported a Pentium D (model 6) and a Xeon - * (model 2) with the same problem. - */ - if (c->x86 == 15) - if (msr_clear_bit(MSR_IA32_MISC_ENABLE, - MSR_IA32_MISC_ENABLE_FAST_STRING_BIT) > 0) - pr_info("kmemcheck: Disabling fast string operations\n"); -#endif - /* * If fast string is not enabled in IA32_MISC_ENABLE for any reason, * clear the fast string and enhanced fast string CPU capabilities. diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile index 7ba7f3d7f477..8e13b8cc6bed 100644 --- a/arch/x86/mm/Makefile +++ b/arch/x86/mm/Makefile @@ -29,8 +29,6 @@ obj-$(CONFIG_X86_PTDUMP) += debug_pagetables.o obj-$(CONFIG_HIGHMEM) += highmem_32.o -obj-$(CONFIG_KMEMCHECK) += kmemcheck/ - KASAN_SANITIZE_kasan_init_$(BITS).o := n obj-$(CONFIG_KASAN) += kasan_init_$(BITS).o diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index ef94620ceb8a..6fdf91ef130a 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -163,12 +163,11 @@ static int page_size_mask; static void __init probe_page_size_mask(void) { /* - * For CONFIG_KMEMCHECK or pagealloc debugging, identity mapping will - * use small pages. + * For pagealloc debugging, identity mapping will use small pages. * This will simplify cpa(), which otherwise needs to support splitting * large pages into small in interrupt context, etc. */ - if (boot_cpu_has(X86_FEATURE_PSE) && !debug_pagealloc_enabled() && !IS_ENABLED(CONFIG_KMEMCHECK)) + if (boot_cpu_has(X86_FEATURE_PSE) && !debug_pagealloc_enabled()) page_size_mask |= 1 << PG_LEVEL_2M; else direct_gbpages = 0; diff --git a/arch/x86/mm/kmemcheck/Makefile b/arch/x86/mm/kmemcheck/Makefile deleted file mode 100644 index 520b3bce4095..000000000000 --- a/arch/x86/mm/kmemcheck/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-y := error.o kmemcheck.o opcode.o pte.o selftest.o shadow.o diff --git a/arch/x86/mm/kmemcheck/error.c b/arch/x86/mm/kmemcheck/error.c index 872ec4159a68..cec594032515 100644 --- a/arch/x86/mm/kmemcheck/error.c +++ b/arch/x86/mm/kmemcheck/error.c @@ -1,228 +1 @@ // SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include -#include -#include -#include -#include - -#include "error.h" -#include "shadow.h" - -enum kmemcheck_error_type { - KMEMCHECK_ERROR_INVALID_ACCESS, - KMEMCHECK_ERROR_BUG, -}; - -#define SHADOW_COPY_SIZE (1 << CONFIG_KMEMCHECK_SHADOW_COPY_SHIFT) - -struct kmemcheck_error { - enum kmemcheck_error_type type; - - union { - /* KMEMCHECK_ERROR_INVALID_ACCESS */ - struct { - /* Kind of access that caused the error */ - enum kmemcheck_shadow state; - /* Address and size of the erroneous read */ - unsigned long address; - unsigned int size; - }; - }; - - struct pt_regs regs; - struct stack_trace trace; - unsigned long trace_entries[32]; - - /* We compress it to a char. */ - unsigned char shadow_copy[SHADOW_COPY_SIZE]; - unsigned char memory_copy[SHADOW_COPY_SIZE]; -}; - -/* - * Create a ring queue of errors to output. We can't call printk() directly - * from the kmemcheck traps, since this may call the console drivers and - * result in a recursive fault. - */ -static struct kmemcheck_error error_fifo[CONFIG_KMEMCHECK_QUEUE_SIZE]; -static unsigned int error_count; -static unsigned int error_rd; -static unsigned int error_wr; -static unsigned int error_missed_count; - -static struct kmemcheck_error *error_next_wr(void) -{ - struct kmemcheck_error *e; - - if (error_count == ARRAY_SIZE(error_fifo)) { - ++error_missed_count; - return NULL; - } - - e = &error_fifo[error_wr]; - if (++error_wr == ARRAY_SIZE(error_fifo)) - error_wr = 0; - ++error_count; - return e; -} - -static struct kmemcheck_error *error_next_rd(void) -{ - struct kmemcheck_error *e; - - if (error_count == 0) - return NULL; - - e = &error_fifo[error_rd]; - if (++error_rd == ARRAY_SIZE(error_fifo)) - error_rd = 0; - --error_count; - return e; -} - -void kmemcheck_error_recall(void) -{ - static const char *desc[] = { - [KMEMCHECK_SHADOW_UNALLOCATED] = "unallocated", - [KMEMCHECK_SHADOW_UNINITIALIZED] = "uninitialized", - [KMEMCHECK_SHADOW_INITIALIZED] = "initialized", - [KMEMCHECK_SHADOW_FREED] = "freed", - }; - - static const char short_desc[] = { - [KMEMCHECK_SHADOW_UNALLOCATED] = 'a', - [KMEMCHECK_SHADOW_UNINITIALIZED] = 'u', - [KMEMCHECK_SHADOW_INITIALIZED] = 'i', - [KMEMCHECK_SHADOW_FREED] = 'f', - }; - - struct kmemcheck_error *e; - unsigned int i; - - e = error_next_rd(); - if (!e) - return; - - switch (e->type) { - case KMEMCHECK_ERROR_INVALID_ACCESS: - printk(KERN_WARNING "WARNING: kmemcheck: Caught %d-bit read from %s memory (%p)\n", - 8 * e->size, e->state < ARRAY_SIZE(desc) ? - desc[e->state] : "(invalid shadow state)", - (void *) e->address); - - printk(KERN_WARNING); - for (i = 0; i < SHADOW_COPY_SIZE; ++i) - printk(KERN_CONT "%02x", e->memory_copy[i]); - printk(KERN_CONT "\n"); - - printk(KERN_WARNING); - for (i = 0; i < SHADOW_COPY_SIZE; ++i) { - if (e->shadow_copy[i] < ARRAY_SIZE(short_desc)) - printk(KERN_CONT " %c", short_desc[e->shadow_copy[i]]); - else - printk(KERN_CONT " ?"); - } - printk(KERN_CONT "\n"); - printk(KERN_WARNING "%*c\n", 2 + 2 - * (int) (e->address & (SHADOW_COPY_SIZE - 1)), '^'); - break; - case KMEMCHECK_ERROR_BUG: - printk(KERN_EMERG "ERROR: kmemcheck: Fatal error\n"); - break; - } - - __show_regs(&e->regs, 1); - print_stack_trace(&e->trace, 0); -} - -static void do_wakeup(unsigned long data) -{ - while (error_count > 0) - kmemcheck_error_recall(); - - if (error_missed_count > 0) { - printk(KERN_WARNING "kmemcheck: Lost %d error reports because " - "the queue was too small\n", error_missed_count); - error_missed_count = 0; - } -} - -static DECLARE_TASKLET(kmemcheck_tasklet, &do_wakeup, 0); - -/* - * Save the context of an error report. - */ -void kmemcheck_error_save(enum kmemcheck_shadow state, - unsigned long address, unsigned int size, struct pt_regs *regs) -{ - static unsigned long prev_ip; - - struct kmemcheck_error *e; - void *shadow_copy; - void *memory_copy; - - /* Don't report several adjacent errors from the same EIP. */ - if (regs->ip == prev_ip) - return; - prev_ip = regs->ip; - - e = error_next_wr(); - if (!e) - return; - - e->type = KMEMCHECK_ERROR_INVALID_ACCESS; - - e->state = state; - e->address = address; - e->size = size; - - /* Save regs */ - memcpy(&e->regs, regs, sizeof(*regs)); - - /* Save stack trace */ - e->trace.nr_entries = 0; - e->trace.entries = e->trace_entries; - e->trace.max_entries = ARRAY_SIZE(e->trace_entries); - e->trace.skip = 0; - save_stack_trace_regs(regs, &e->trace); - - /* Round address down to nearest 16 bytes */ - shadow_copy = kmemcheck_shadow_lookup(address - & ~(SHADOW_COPY_SIZE - 1)); - BUG_ON(!shadow_copy); - - memcpy(e->shadow_copy, shadow_copy, SHADOW_COPY_SIZE); - - kmemcheck_show_addr(address); - memory_copy = (void *) (address & ~(SHADOW_COPY_SIZE - 1)); - memcpy(e->memory_copy, memory_copy, SHADOW_COPY_SIZE); - kmemcheck_hide_addr(address); - - tasklet_hi_schedule_first(&kmemcheck_tasklet); -} - -/* - * Save the context of a kmemcheck bug. - */ -void kmemcheck_error_save_bug(struct pt_regs *regs) -{ - struct kmemcheck_error *e; - - e = error_next_wr(); - if (!e) - return; - - e->type = KMEMCHECK_ERROR_BUG; - - memcpy(&e->regs, regs, sizeof(*regs)); - - e->trace.nr_entries = 0; - e->trace.entries = e->trace_entries; - e->trace.max_entries = ARRAY_SIZE(e->trace_entries); - e->trace.skip = 1; - save_stack_trace(&e->trace); - - tasklet_hi_schedule_first(&kmemcheck_tasklet); -} diff --git a/arch/x86/mm/kmemcheck/error.h b/arch/x86/mm/kmemcheck/error.h index 39f80d7a874d..ea32a7d3cf1b 100644 --- a/arch/x86/mm/kmemcheck/error.h +++ b/arch/x86/mm/kmemcheck/error.h @@ -1,16 +1 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#ifndef ARCH__X86__MM__KMEMCHECK__ERROR_H -#define ARCH__X86__MM__KMEMCHECK__ERROR_H - -#include - -#include "shadow.h" - -void kmemcheck_error_save(enum kmemcheck_shadow state, - unsigned long address, unsigned int size, struct pt_regs *regs); - -void kmemcheck_error_save_bug(struct pt_regs *regs); - -void kmemcheck_error_recall(void); - -#endif diff --git a/arch/x86/mm/kmemcheck/kmemcheck.c b/arch/x86/mm/kmemcheck/kmemcheck.c deleted file mode 100644 index 4515bae36bbe..000000000000 --- a/arch/x86/mm/kmemcheck/kmemcheck.c +++ /dev/null @@ -1,658 +0,0 @@ -/** - * kmemcheck - a heavyweight memory checker for the linux kernel - * Copyright (C) 2007, 2008 Vegard Nossum - * (With a lot of help from Ingo Molnar and Pekka Enberg.) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License (version 2) as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "error.h" -#include "opcode.h" -#include "pte.h" -#include "selftest.h" -#include "shadow.h" - - -#ifdef CONFIG_KMEMCHECK_DISABLED_BY_DEFAULT -# define KMEMCHECK_ENABLED 0 -#endif - -#ifdef CONFIG_KMEMCHECK_ENABLED_BY_DEFAULT -# define KMEMCHECK_ENABLED 1 -#endif - -#ifdef CONFIG_KMEMCHECK_ONESHOT_BY_DEFAULT -# define KMEMCHECK_ENABLED 2 -#endif - -int kmemcheck_enabled = KMEMCHECK_ENABLED; - -int __init kmemcheck_init(void) -{ -#ifdef CONFIG_SMP - /* - * Limit SMP to use a single CPU. We rely on the fact that this code - * runs before SMP is set up. - */ - if (setup_max_cpus > 1) { - printk(KERN_INFO - "kmemcheck: Limiting number of CPUs to 1.\n"); - setup_max_cpus = 1; - } -#endif - - if (!kmemcheck_selftest()) { - printk(KERN_INFO "kmemcheck: self-tests failed; disabling\n"); - kmemcheck_enabled = 0; - return -EINVAL; - } - - printk(KERN_INFO "kmemcheck: Initialized\n"); - return 0; -} - -early_initcall(kmemcheck_init); - -/* - * We need to parse the kmemcheck= option before any memory is allocated. - */ -static int __init param_kmemcheck(char *str) -{ - int val; - int ret; - - if (!str) - return -EINVAL; - - ret = kstrtoint(str, 0, &val); - if (ret) - return ret; - kmemcheck_enabled = val; - return 0; -} - -early_param("kmemcheck", param_kmemcheck); - -int kmemcheck_show_addr(unsigned long address) -{ - pte_t *pte; - - pte = kmemcheck_pte_lookup(address); - if (!pte) - return 0; - - set_pte(pte, __pte(pte_val(*pte) | _PAGE_PRESENT)); - __flush_tlb_one(address); - return 1; -} - -int kmemcheck_hide_addr(unsigned long address) -{ - pte_t *pte; - - pte = kmemcheck_pte_lookup(address); - if (!pte) - return 0; - - set_pte(pte, __pte(pte_val(*pte) & ~_PAGE_PRESENT)); - __flush_tlb_one(address); - return 1; -} - -struct kmemcheck_context { - bool busy; - int balance; - - /* - * There can be at most two memory operands to an instruction, but - * each address can cross a page boundary -- so we may need up to - * four addresses that must be hidden/revealed for each fault. - */ - unsigned long addr[4]; - unsigned long n_addrs; - unsigned long flags; - - /* Data size of the instruction that caused a fault. */ - unsigned int size; -}; - -static DEFINE_PER_CPU(struct kmemcheck_context, kmemcheck_context); - -bool kmemcheck_active(struct pt_regs *regs) -{ - struct kmemcheck_context *data = this_cpu_ptr(&kmemcheck_context); - - return data->balance > 0; -} - -/* Save an address that needs to be shown/hidden */ -static void kmemcheck_save_addr(unsigned long addr) -{ - struct kmemcheck_context *data = this_cpu_ptr(&kmemcheck_context); - - BUG_ON(data->n_addrs >= ARRAY_SIZE(data->addr)); - data->addr[data->n_addrs++] = addr; -} - -static unsigned int kmemcheck_show_all(void) -{ - struct kmemcheck_context *data = this_cpu_ptr(&kmemcheck_context); - unsigned int i; - unsigned int n; - - n = 0; - for (i = 0; i < data->n_addrs; ++i) - n += kmemcheck_show_addr(data->addr[i]); - - return n; -} - -static unsigned int kmemcheck_hide_all(void) -{ - struct kmemcheck_context *data = this_cpu_ptr(&kmemcheck_context); - unsigned int i; - unsigned int n; - - n = 0; - for (i = 0; i < data->n_addrs; ++i) - n += kmemcheck_hide_addr(data->addr[i]); - - return n; -} - -/* - * Called from the #PF handler. - */ -void kmemcheck_show(struct pt_regs *regs) -{ - struct kmemcheck_context *data = this_cpu_ptr(&kmemcheck_context); - - BUG_ON(!irqs_disabled()); - - if (unlikely(data->balance != 0)) { - kmemcheck_show_all(); - kmemcheck_error_save_bug(regs); - data->balance = 0; - return; - } - - /* - * None of the addresses actually belonged to kmemcheck. Note that - * this is not an error. - */ - if (kmemcheck_show_all() == 0) - return; - - ++data->balance; - - /* - * The IF needs to be cleared as well, so that the faulting - * instruction can run "uninterrupted". Otherwise, we might take - * an interrupt and start executing that before we've had a chance - * to hide the page again. - * - * NOTE: In the rare case of multiple faults, we must not override - * the original flags: - */ - if (!(regs->flags & X86_EFLAGS_TF)) - data->flags = regs->flags; - - regs->flags |= X86_EFLAGS_TF; - regs->flags &= ~X86_EFLAGS_IF; -} - -/* - * Called from the #DB handler. - */ -void kmemcheck_hide(struct pt_regs *regs) -{ - struct kmemcheck_context *data = this_cpu_ptr(&kmemcheck_context); - int n; - - BUG_ON(!irqs_disabled()); - - if (unlikely(data->balance != 1)) { - kmemcheck_show_all(); - kmemcheck_error_save_bug(regs); - data->n_addrs = 0; - data->balance = 0; - - if (!(data->flags & X86_EFLAGS_TF)) - regs->flags &= ~X86_EFLAGS_TF; - if (data->flags & X86_EFLAGS_IF) - regs->flags |= X86_EFLAGS_IF; - return; - } - - if (kmemcheck_enabled) - n = kmemcheck_hide_all(); - else - n = kmemcheck_show_all(); - - if (n == 0) - return; - - --data->balance; - - data->n_addrs = 0; - - if (!(data->flags & X86_EFLAGS_TF)) - regs->flags &= ~X86_EFLAGS_TF; - if (data->flags & X86_EFLAGS_IF) - regs->flags |= X86_EFLAGS_IF; -} - -void kmemcheck_show_pages(struct page *p, unsigned int n) -{ - unsigned int i; - - for (i = 0; i < n; ++i) { - unsigned long address; - pte_t *pte; - unsigned int level; - - address = (unsigned long) page_address(&p[i]); - pte = lookup_address(address, &level); - BUG_ON(!pte); - BUG_ON(level != PG_LEVEL_4K); - - set_pte(pte, __pte(pte_val(*pte) | _PAGE_PRESENT)); - set_pte(pte, __pte(pte_val(*pte) & ~_PAGE_HIDDEN)); - __flush_tlb_one(address); - } -} - -bool kmemcheck_page_is_tracked(struct page *p) -{ - /* This will also check the "hidden" flag of the PTE. */ - return kmemcheck_pte_lookup((unsigned long) page_address(p)); -} - -void kmemcheck_hide_pages(struct page *p, unsigned int n) -{ - unsigned int i; - - for (i = 0; i < n; ++i) { - unsigned long address; - pte_t *pte; - unsigned int level; - - address = (unsigned long) page_address(&p[i]); - pte = lookup_address(address, &level); - BUG_ON(!pte); - BUG_ON(level != PG_LEVEL_4K); - - set_pte(pte, __pte(pte_val(*pte) & ~_PAGE_PRESENT)); - set_pte(pte, __pte(pte_val(*pte) | _PAGE_HIDDEN)); - __flush_tlb_one(address); - } -} - -/* Access may NOT cross page boundary */ -static void kmemcheck_read_strict(struct pt_regs *regs, - unsigned long addr, unsigned int size) -{ - void *shadow; - enum kmemcheck_shadow status; - - shadow = kmemcheck_shadow_lookup(addr); - if (!shadow) - return; - - kmemcheck_save_addr(addr); - status = kmemcheck_shadow_test(shadow, size); - if (status == KMEMCHECK_SHADOW_INITIALIZED) - return; - - if (kmemcheck_enabled) - kmemcheck_error_save(status, addr, size, regs); - - if (kmemcheck_enabled == 2) - kmemcheck_enabled = 0; - - /* Don't warn about it again. */ - kmemcheck_shadow_set(shadow, size); -} - -bool kmemcheck_is_obj_initialized(unsigned long addr, size_t size) -{ - enum kmemcheck_shadow status; - void *shadow; - - shadow = kmemcheck_shadow_lookup(addr); - if (!shadow) - return true; - - status = kmemcheck_shadow_test_all(shadow, size); - - return status == KMEMCHECK_SHADOW_INITIALIZED; -} - -/* Access may cross page boundary */ -static void kmemcheck_read(struct pt_regs *regs, - unsigned long addr, unsigned int size) -{ - unsigned long page = addr & PAGE_MASK; - unsigned long next_addr = addr + size - 1; - unsigned long next_page = next_addr & PAGE_MASK; - - if (likely(page == next_page)) { - kmemcheck_read_strict(regs, addr, size); - return; - } - - /* - * What we do is basically to split the access across the - * two pages and handle each part separately. Yes, this means - * that we may now see reads that are 3 + 5 bytes, for - * example (and if both are uninitialized, there will be two - * reports), but it makes the code a lot simpler. - */ - kmemcheck_read_strict(regs, addr, next_page - addr); - kmemcheck_read_strict(regs, next_page, next_addr - next_page); -} - -static void kmemcheck_write_strict(struct pt_regs *regs, - unsigned long addr, unsigned int size) -{ - void *shadow; - - shadow = kmemcheck_shadow_lookup(addr); - if (!shadow) - return; - - kmemcheck_save_addr(addr); - kmemcheck_shadow_set(shadow, size); -} - -static void kmemcheck_write(struct pt_regs *regs, - unsigned long addr, unsigned int size) -{ - unsigned long page = addr & PAGE_MASK; - unsigned long next_addr = addr + size - 1; - unsigned long next_page = next_addr & PAGE_MASK; - - if (likely(page == next_page)) { - kmemcheck_write_strict(regs, addr, size); - return; - } - - /* See comment in kmemcheck_read(). */ - kmemcheck_write_strict(regs, addr, next_page - addr); - kmemcheck_write_strict(regs, next_page, next_addr - next_page); -} - -/* - * Copying is hard. We have two addresses, each of which may be split across - * a page (and each page will have different shadow addresses). - */ -static void kmemcheck_copy(struct pt_regs *regs, - unsigned long src_addr, unsigned long dst_addr, unsigned int size) -{ - uint8_t shadow[8]; - enum kmemcheck_shadow status; - - unsigned long page; - unsigned long next_addr; - unsigned long next_page; - - uint8_t *x; - unsigned int i; - unsigned int n; - - BUG_ON(size > sizeof(shadow)); - - page = src_addr & PAGE_MASK; - next_addr = src_addr + size - 1; - next_page = next_addr & PAGE_MASK; - - if (likely(page == next_page)) { - /* Same page */ - x = kmemcheck_shadow_lookup(src_addr); - if (x) { - kmemcheck_save_addr(src_addr); - for (i = 0; i < size; ++i) - shadow[i] = x[i]; - } else { - for (i = 0; i < size; ++i) - shadow[i] = KMEMCHECK_SHADOW_INITIALIZED; - } - } else { - n = next_page - src_addr; - BUG_ON(n > sizeof(shadow)); - - /* First page */ - x = kmemcheck_shadow_lookup(src_addr); - if (x) { - kmemcheck_save_addr(src_addr); - for (i = 0; i < n; ++i) - shadow[i] = x[i]; - } else { - /* Not tracked */ - for (i = 0; i < n; ++i) - shadow[i] = KMEMCHECK_SHADOW_INITIALIZED; - } - - /* Second page */ - x = kmemcheck_shadow_lookup(next_page); - if (x) { - kmemcheck_save_addr(next_page); - for (i = n; i < size; ++i) - shadow[i] = x[i - n]; - } else { - /* Not tracked */ - for (i = n; i < size; ++i) - shadow[i] = KMEMCHECK_SHADOW_INITIALIZED; - } - } - - page = dst_addr & PAGE_MASK; - next_addr = dst_addr + size - 1; - next_page = next_addr & PAGE_MASK; - - if (likely(page == next_page)) { - /* Same page */ - x = kmemcheck_shadow_lookup(dst_addr); - if (x) { - kmemcheck_save_addr(dst_addr); - for (i = 0; i < size; ++i) { - x[i] = shadow[i]; - shadow[i] = KMEMCHECK_SHADOW_INITIALIZED; - } - } - } else { - n = next_page - dst_addr; - BUG_ON(n > sizeof(shadow)); - - /* First page */ - x = kmemcheck_shadow_lookup(dst_addr); - if (x) { - kmemcheck_save_addr(dst_addr); - for (i = 0; i < n; ++i) { - x[i] = shadow[i]; - shadow[i] = KMEMCHECK_SHADOW_INITIALIZED; - } - } - - /* Second page */ - x = kmemcheck_shadow_lookup(next_page); - if (x) { - kmemcheck_save_addr(next_page); - for (i = n; i < size; ++i) { - x[i - n] = shadow[i]; - shadow[i] = KMEMCHECK_SHADOW_INITIALIZED; - } - } - } - - status = kmemcheck_shadow_test(shadow, size); - if (status == KMEMCHECK_SHADOW_INITIALIZED) - return; - - if (kmemcheck_enabled) - kmemcheck_error_save(status, src_addr, size, regs); - - if (kmemcheck_enabled == 2) - kmemcheck_enabled = 0; -} - -enum kmemcheck_method { - KMEMCHECK_READ, - KMEMCHECK_WRITE, -}; - -static void kmemcheck_access(struct pt_regs *regs, - unsigned long fallback_address, enum kmemcheck_method fallback_method) -{ - const uint8_t *insn; - const uint8_t *insn_primary; - unsigned int size; - - struct kmemcheck_context *data = this_cpu_ptr(&kmemcheck_context); - - /* Recursive fault -- ouch. */ - if (data->busy) { - kmemcheck_show_addr(fallback_address); - kmemcheck_error_save_bug(regs); - return; - } - - data->busy = true; - - insn = (const uint8_t *) regs->ip; - insn_primary = kmemcheck_opcode_get_primary(insn); - - kmemcheck_opcode_decode(insn, &size); - - switch (insn_primary[0]) { -#ifdef CONFIG_KMEMCHECK_BITOPS_OK - /* AND, OR, XOR */ - /* - * Unfortunately, these instructions have to be excluded from - * our regular checking since they access only some (and not - * all) bits. This clears out "bogus" bitfield-access warnings. - */ - case 0x80: - case 0x81: - case 0x82: - case 0x83: - switch ((insn_primary[1] >> 3) & 7) { - /* OR */ - case 1: - /* AND */ - case 4: - /* XOR */ - case 6: - kmemcheck_write(regs, fallback_address, size); - goto out; - - /* ADD */ - case 0: - /* ADC */ - case 2: - /* SBB */ - case 3: - /* SUB */ - case 5: - /* CMP */ - case 7: - break; - } - break; -#endif - - /* MOVS, MOVSB, MOVSW, MOVSD */ - case 0xa4: - case 0xa5: - /* - * These instructions are special because they take two - * addresses, but we only get one page fault. - */ - kmemcheck_copy(regs, regs->si, regs->di, size); - goto out; - - /* CMPS, CMPSB, CMPSW, CMPSD */ - case 0xa6: - case 0xa7: - kmemcheck_read(regs, regs->si, size); - kmemcheck_read(regs, regs->di, size); - goto out; - } - - /* - * If the opcode isn't special in any way, we use the data from the - * page fault handler to determine the address and type of memory - * access. - */ - switch (fallback_method) { - case KMEMCHECK_READ: - kmemcheck_read(regs, fallback_address, size); - goto out; - case KMEMCHECK_WRITE: - kmemcheck_write(regs, fallback_address, size); - goto out; - } - -out: - data->busy = false; -} - -bool kmemcheck_fault(struct pt_regs *regs, unsigned long address, - unsigned long error_code) -{ - pte_t *pte; - - /* - * XXX: Is it safe to assume that memory accesses from virtual 86 - * mode or non-kernel code segments will _never_ access kernel - * memory (e.g. tracked pages)? For now, we need this to avoid - * invoking kmemcheck for PnP BIOS calls. - */ - if (regs->flags & X86_VM_MASK) - return false; - if (regs->cs != __KERNEL_CS) - return false; - - pte = kmemcheck_pte_lookup(address); - if (!pte) - return false; - - WARN_ON_ONCE(in_nmi()); - - if (error_code & 2) - kmemcheck_access(regs, address, KMEMCHECK_WRITE); - else - kmemcheck_access(regs, address, KMEMCHECK_READ); - - kmemcheck_show(regs); - return true; -} - -bool kmemcheck_trap(struct pt_regs *regs) -{ - if (!kmemcheck_active(regs)) - return false; - - /* We're done. */ - kmemcheck_hide(regs); - return true; -} diff --git a/arch/x86/mm/kmemcheck/opcode.c b/arch/x86/mm/kmemcheck/opcode.c index df8109ddf7fe..cec594032515 100644 --- a/arch/x86/mm/kmemcheck/opcode.c +++ b/arch/x86/mm/kmemcheck/opcode.c @@ -1,107 +1 @@ // SPDX-License-Identifier: GPL-2.0 -#include - -#include "opcode.h" - -static bool opcode_is_prefix(uint8_t b) -{ - return - /* Group 1 */ - b == 0xf0 || b == 0xf2 || b == 0xf3 - /* Group 2 */ - || b == 0x2e || b == 0x36 || b == 0x3e || b == 0x26 - || b == 0x64 || b == 0x65 - /* Group 3 */ - || b == 0x66 - /* Group 4 */ - || b == 0x67; -} - -#ifdef CONFIG_X86_64 -static bool opcode_is_rex_prefix(uint8_t b) -{ - return (b & 0xf0) == 0x40; -} -#else -static bool opcode_is_rex_prefix(uint8_t b) -{ - return false; -} -#endif - -#define REX_W (1 << 3) - -/* - * This is a VERY crude opcode decoder. We only need to find the size of the - * load/store that caused our #PF and this should work for all the opcodes - * that we care about. Moreover, the ones who invented this instruction set - * should be shot. - */ -void kmemcheck_opcode_decode(const uint8_t *op, unsigned int *size) -{ - /* Default operand size */ - int operand_size_override = 4; - - /* prefixes */ - for (; opcode_is_prefix(*op); ++op) { - if (*op == 0x66) - operand_size_override = 2; - } - - /* REX prefix */ - if (opcode_is_rex_prefix(*op)) { - uint8_t rex = *op; - - ++op; - if (rex & REX_W) { - switch (*op) { - case 0x63: - *size = 4; - return; - case 0x0f: - ++op; - - switch (*op) { - case 0xb6: - case 0xbe: - *size = 1; - return; - case 0xb7: - case 0xbf: - *size = 2; - return; - } - - break; - } - - *size = 8; - return; - } - } - - /* escape opcode */ - if (*op == 0x0f) { - ++op; - - /* - * This is move with zero-extend and sign-extend, respectively; - * we don't have to think about 0xb6/0xbe, because this is - * already handled in the conditional below. - */ - if (*op == 0xb7 || *op == 0xbf) - operand_size_override = 2; - } - - *size = (*op & 1) ? operand_size_override : 1; -} - -const uint8_t *kmemcheck_opcode_get_primary(const uint8_t *op) -{ - /* skip prefixes */ - while (opcode_is_prefix(*op)) - ++op; - if (opcode_is_rex_prefix(*op)) - ++op; - return op; -} diff --git a/arch/x86/mm/kmemcheck/opcode.h b/arch/x86/mm/kmemcheck/opcode.h index 51a1ce94c24a..ea32a7d3cf1b 100644 --- a/arch/x86/mm/kmemcheck/opcode.h +++ b/arch/x86/mm/kmemcheck/opcode.h @@ -1,10 +1 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#ifndef ARCH__X86__MM__KMEMCHECK__OPCODE_H -#define ARCH__X86__MM__KMEMCHECK__OPCODE_H - -#include - -void kmemcheck_opcode_decode(const uint8_t *op, unsigned int *size); -const uint8_t *kmemcheck_opcode_get_primary(const uint8_t *op); - -#endif diff --git a/arch/x86/mm/kmemcheck/pte.c b/arch/x86/mm/kmemcheck/pte.c index 8a03be90272a..cec594032515 100644 --- a/arch/x86/mm/kmemcheck/pte.c +++ b/arch/x86/mm/kmemcheck/pte.c @@ -1,23 +1 @@ // SPDX-License-Identifier: GPL-2.0 -#include - -#include - -#include "pte.h" - -pte_t *kmemcheck_pte_lookup(unsigned long address) -{ - pte_t *pte; - unsigned int level; - - pte = lookup_address(address, &level); - if (!pte) - return NULL; - if (level != PG_LEVEL_4K) - return NULL; - if (!pte_hidden(*pte)) - return NULL; - - return pte; -} - diff --git a/arch/x86/mm/kmemcheck/pte.h b/arch/x86/mm/kmemcheck/pte.h index b595612382c2..ea32a7d3cf1b 100644 --- a/arch/x86/mm/kmemcheck/pte.h +++ b/arch/x86/mm/kmemcheck/pte.h @@ -1,11 +1 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#ifndef ARCH__X86__MM__KMEMCHECK__PTE_H -#define ARCH__X86__MM__KMEMCHECK__PTE_H - -#include - -#include - -pte_t *kmemcheck_pte_lookup(unsigned long address); - -#endif diff --git a/arch/x86/mm/kmemcheck/selftest.c b/arch/x86/mm/kmemcheck/selftest.c index 7ce0be1f99eb..cec594032515 100644 --- a/arch/x86/mm/kmemcheck/selftest.c +++ b/arch/x86/mm/kmemcheck/selftest.c @@ -1,71 +1 @@ // SPDX-License-Identifier: GPL-2.0 -#include -#include - -#include "opcode.h" -#include "selftest.h" - -struct selftest_opcode { - unsigned int expected_size; - const uint8_t *insn; - const char *desc; -}; - -static const struct selftest_opcode selftest_opcodes[] = { - /* REP MOVS */ - {1, "\xf3\xa4", "rep movsb , "}, - {4, "\xf3\xa5", "rep movsl , "}, - - /* MOVZX / MOVZXD */ - {1, "\x66\x0f\xb6\x51\xf8", "movzwq , "}, - {1, "\x0f\xb6\x51\xf8", "movzwq , "}, - - /* MOVSX / MOVSXD */ - {1, "\x66\x0f\xbe\x51\xf8", "movswq , "}, - {1, "\x0f\xbe\x51\xf8", "movswq , "}, - -#ifdef CONFIG_X86_64 - /* MOVZX / MOVZXD */ - {1, "\x49\x0f\xb6\x51\xf8", "movzbq , "}, - {2, "\x49\x0f\xb7\x51\xf8", "movzbq , "}, - - /* MOVSX / MOVSXD */ - {1, "\x49\x0f\xbe\x51\xf8", "movsbq , "}, - {2, "\x49\x0f\xbf\x51\xf8", "movsbq , "}, - {4, "\x49\x63\x51\xf8", "movslq , "}, -#endif -}; - -static bool selftest_opcode_one(const struct selftest_opcode *op) -{ - unsigned size; - - kmemcheck_opcode_decode(op->insn, &size); - - if (size == op->expected_size) - return true; - - printk(KERN_WARNING "kmemcheck: opcode %s: expected size %d, got %d\n", - op->desc, op->expected_size, size); - return false; -} - -static bool selftest_opcodes_all(void) -{ - bool pass = true; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(selftest_opcodes); ++i) - pass = pass && selftest_opcode_one(&selftest_opcodes[i]); - - return pass; -} - -bool kmemcheck_selftest(void) -{ - bool pass = true; - - pass = pass && selftest_opcodes_all(); - - return pass; -} diff --git a/arch/x86/mm/kmemcheck/selftest.h b/arch/x86/mm/kmemcheck/selftest.h index 8d759aae453d..ea32a7d3cf1b 100644 --- a/arch/x86/mm/kmemcheck/selftest.h +++ b/arch/x86/mm/kmemcheck/selftest.h @@ -1,7 +1 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#ifndef ARCH_X86_MM_KMEMCHECK_SELFTEST_H -#define ARCH_X86_MM_KMEMCHECK_SELFTEST_H - -bool kmemcheck_selftest(void); - -#endif diff --git a/arch/x86/mm/kmemcheck/shadow.c b/arch/x86/mm/kmemcheck/shadow.c deleted file mode 100644 index c2638a7d2c10..000000000000 --- a/arch/x86/mm/kmemcheck/shadow.c +++ /dev/null @@ -1,173 +0,0 @@ -#include -#include -#include - -#include -#include - -#include "pte.h" -#include "shadow.h" - -/* - * Return the shadow address for the given address. Returns NULL if the - * address is not tracked. - * - * We need to be extremely careful not to follow any invalid pointers, - * because this function can be called for *any* possible address. - */ -void *kmemcheck_shadow_lookup(unsigned long address) -{ - pte_t *pte; - struct page *page; - - if (!virt_addr_valid(address)) - return NULL; - - pte = kmemcheck_pte_lookup(address); - if (!pte) - return NULL; - - page = virt_to_page(address); - if (!page->shadow) - return NULL; - return page->shadow + (address & (PAGE_SIZE - 1)); -} - -static void mark_shadow(void *address, unsigned int n, - enum kmemcheck_shadow status) -{ - unsigned long addr = (unsigned long) address; - unsigned long last_addr = addr + n - 1; - unsigned long page = addr & PAGE_MASK; - unsigned long last_page = last_addr & PAGE_MASK; - unsigned int first_n; - void *shadow; - - /* If the memory range crosses a page boundary, stop there. */ - if (page == last_page) - first_n = n; - else - first_n = page + PAGE_SIZE - addr; - - shadow = kmemcheck_shadow_lookup(addr); - if (shadow) - memset(shadow, status, first_n); - - addr += first_n; - n -= first_n; - - /* Do full-page memset()s. */ - while (n >= PAGE_SIZE) { - shadow = kmemcheck_shadow_lookup(addr); - if (shadow) - memset(shadow, status, PAGE_SIZE); - - addr += PAGE_SIZE; - n -= PAGE_SIZE; - } - - /* Do the remaining page, if any. */ - if (n > 0) { - shadow = kmemcheck_shadow_lookup(addr); - if (shadow) - memset(shadow, status, n); - } -} - -void kmemcheck_mark_unallocated(void *address, unsigned int n) -{ - mark_shadow(address, n, KMEMCHECK_SHADOW_UNALLOCATED); -} - -void kmemcheck_mark_uninitialized(void *address, unsigned int n) -{ - mark_shadow(address, n, KMEMCHECK_SHADOW_UNINITIALIZED); -} - -/* - * Fill the shadow memory of the given address such that the memory at that - * address is marked as being initialized. - */ -void kmemcheck_mark_initialized(void *address, unsigned int n) -{ - mark_shadow(address, n, KMEMCHECK_SHADOW_INITIALIZED); -} -EXPORT_SYMBOL_GPL(kmemcheck_mark_initialized); - -void kmemcheck_mark_freed(void *address, unsigned int n) -{ - mark_shadow(address, n, KMEMCHECK_SHADOW_FREED); -} - -void kmemcheck_mark_unallocated_pages(struct page *p, unsigned int n) -{ - unsigned int i; - - for (i = 0; i < n; ++i) - kmemcheck_mark_unallocated(page_address(&p[i]), PAGE_SIZE); -} - -void kmemcheck_mark_uninitialized_pages(struct page *p, unsigned int n) -{ - unsigned int i; - - for (i = 0; i < n; ++i) - kmemcheck_mark_uninitialized(page_address(&p[i]), PAGE_SIZE); -} - -void kmemcheck_mark_initialized_pages(struct page *p, unsigned int n) -{ - unsigned int i; - - for (i = 0; i < n; ++i) - kmemcheck_mark_initialized(page_address(&p[i]), PAGE_SIZE); -} - -enum kmemcheck_shadow kmemcheck_shadow_test(void *shadow, unsigned int size) -{ -#ifdef CONFIG_KMEMCHECK_PARTIAL_OK - uint8_t *x; - unsigned int i; - - x = shadow; - - /* - * Make sure _some_ bytes are initialized. Gcc frequently generates - * code to access neighboring bytes. - */ - for (i = 0; i < size; ++i) { - if (x[i] == KMEMCHECK_SHADOW_INITIALIZED) - return x[i]; - } - - return x[0]; -#else - return kmemcheck_shadow_test_all(shadow, size); -#endif -} - -enum kmemcheck_shadow kmemcheck_shadow_test_all(void *shadow, unsigned int size) -{ - uint8_t *x; - unsigned int i; - - x = shadow; - - /* All bytes must be initialized. */ - for (i = 0; i < size; ++i) { - if (x[i] != KMEMCHECK_SHADOW_INITIALIZED) - return x[i]; - } - - return x[0]; -} - -void kmemcheck_shadow_set(void *shadow, unsigned int size) -{ - uint8_t *x; - unsigned int i; - - x = shadow; - for (i = 0; i < size; ++i) - x[i] = KMEMCHECK_SHADOW_INITIALIZED; -} diff --git a/arch/x86/mm/kmemcheck/shadow.h b/arch/x86/mm/kmemcheck/shadow.h index 49768dc18664..ea32a7d3cf1b 100644 --- a/arch/x86/mm/kmemcheck/shadow.h +++ b/arch/x86/mm/kmemcheck/shadow.h @@ -1,19 +1 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#ifndef ARCH__X86__MM__KMEMCHECK__SHADOW_H -#define ARCH__X86__MM__KMEMCHECK__SHADOW_H - -enum kmemcheck_shadow { - KMEMCHECK_SHADOW_UNALLOCATED, - KMEMCHECK_SHADOW_UNINITIALIZED, - KMEMCHECK_SHADOW_INITIALIZED, - KMEMCHECK_SHADOW_FREED, -}; - -void *kmemcheck_shadow_lookup(unsigned long address); - -enum kmemcheck_shadow kmemcheck_shadow_test(void *shadow, unsigned int size); -enum kmemcheck_shadow kmemcheck_shadow_test_all(void *shadow, - unsigned int size); -void kmemcheck_shadow_set(void *shadow, unsigned int size); - -#endif diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index baeb872283d9..69c238210325 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -594,21 +594,6 @@ static inline void tasklet_hi_schedule(struct tasklet_struct *t) __tasklet_hi_schedule(t); } -extern void __tasklet_hi_schedule_first(struct tasklet_struct *t); - -/* - * This version avoids touching any other tasklets. Needed for kmemcheck - * in order not to take any page faults while enqueueing this tasklet; - * consider VERY carefully whether you really need this or - * tasklet_hi_schedule()... - */ -static inline void tasklet_hi_schedule_first(struct tasklet_struct *t) -{ - if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) - __tasklet_hi_schedule_first(t); -} - - static inline void tasklet_disable_nosync(struct tasklet_struct *t) { atomic_inc(&t->count); diff --git a/include/linux/kmemcheck.h b/include/linux/kmemcheck.h index 7b1d7bead7d9..ea32a7d3cf1b 100644 --- a/include/linux/kmemcheck.h +++ b/include/linux/kmemcheck.h @@ -1,172 +1 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#ifndef LINUX_KMEMCHECK_H -#define LINUX_KMEMCHECK_H - -#include -#include - -#ifdef CONFIG_KMEMCHECK -extern int kmemcheck_enabled; - -/* The slab-related functions. */ -void kmemcheck_alloc_shadow(struct page *page, int order, gfp_t flags, int node); -void kmemcheck_free_shadow(struct page *page, int order); -void kmemcheck_slab_alloc(struct kmem_cache *s, gfp_t gfpflags, void *object, - size_t size); -void kmemcheck_slab_free(struct kmem_cache *s, void *object, size_t size); - -void kmemcheck_pagealloc_alloc(struct page *p, unsigned int order, - gfp_t gfpflags); - -void kmemcheck_show_pages(struct page *p, unsigned int n); -void kmemcheck_hide_pages(struct page *p, unsigned int n); - -bool kmemcheck_page_is_tracked(struct page *p); - -void kmemcheck_mark_unallocated(void *address, unsigned int n); -void kmemcheck_mark_uninitialized(void *address, unsigned int n); -void kmemcheck_mark_initialized(void *address, unsigned int n); -void kmemcheck_mark_freed(void *address, unsigned int n); - -void kmemcheck_mark_unallocated_pages(struct page *p, unsigned int n); -void kmemcheck_mark_uninitialized_pages(struct page *p, unsigned int n); -void kmemcheck_mark_initialized_pages(struct page *p, unsigned int n); - -int kmemcheck_show_addr(unsigned long address); -int kmemcheck_hide_addr(unsigned long address); - -bool kmemcheck_is_obj_initialized(unsigned long addr, size_t size); - -/* - * Bitfield annotations - * - * How to use: If you have a struct using bitfields, for example - * - * struct a { - * int x:8, y:8; - * }; - * - * then this should be rewritten as - * - * struct a { - * kmemcheck_bitfield_begin(flags); - * int x:8, y:8; - * kmemcheck_bitfield_end(flags); - * }; - * - * Now the "flags_begin" and "flags_end" members may be used to refer to the - * beginning and end, respectively, of the bitfield (and things like - * &x.flags_begin is allowed). As soon as the struct is allocated, the bit- - * fields should be annotated: - * - * struct a *a = kmalloc(sizeof(struct a), GFP_KERNEL); - * kmemcheck_annotate_bitfield(a, flags); - */ -#define kmemcheck_bitfield_begin(name) \ - int name##_begin[0]; - -#define kmemcheck_bitfield_end(name) \ - int name##_end[0]; - -#define kmemcheck_annotate_bitfield(ptr, name) \ - do { \ - int _n; \ - \ - if (!ptr) \ - break; \ - \ - _n = (long) &((ptr)->name##_end) \ - - (long) &((ptr)->name##_begin); \ - BUILD_BUG_ON(_n < 0); \ - \ - kmemcheck_mark_initialized(&((ptr)->name##_begin), _n); \ - } while (0) - -#define kmemcheck_annotate_variable(var) \ - do { \ - kmemcheck_mark_initialized(&(var), sizeof(var)); \ - } while (0) \ - -#else -#define kmemcheck_enabled 0 - -static inline void -kmemcheck_alloc_shadow(struct page *page, int order, gfp_t flags, int node) -{ -} - -static inline void -kmemcheck_free_shadow(struct page *page, int order) -{ -} - -static inline void -kmemcheck_slab_alloc(struct kmem_cache *s, gfp_t gfpflags, void *object, - size_t size) -{ -} - -static inline void kmemcheck_slab_free(struct kmem_cache *s, void *object, - size_t size) -{ -} - -static inline void kmemcheck_pagealloc_alloc(struct page *p, - unsigned int order, gfp_t gfpflags) -{ -} - -static inline bool kmemcheck_page_is_tracked(struct page *p) -{ - return false; -} - -static inline void kmemcheck_mark_unallocated(void *address, unsigned int n) -{ -} - -static inline void kmemcheck_mark_uninitialized(void *address, unsigned int n) -{ -} - -static inline void kmemcheck_mark_initialized(void *address, unsigned int n) -{ -} - -static inline void kmemcheck_mark_freed(void *address, unsigned int n) -{ -} - -static inline void kmemcheck_mark_unallocated_pages(struct page *p, - unsigned int n) -{ -} - -static inline void kmemcheck_mark_uninitialized_pages(struct page *p, - unsigned int n) -{ -} - -static inline void kmemcheck_mark_initialized_pages(struct page *p, - unsigned int n) -{ -} - -static inline bool kmemcheck_is_obj_initialized(unsigned long addr, size_t size) -{ - return true; -} - -#define kmemcheck_bitfield_begin(name) -#define kmemcheck_bitfield_end(name) -#define kmemcheck_annotate_bitfield(ptr, name) \ - do { \ - } while (0) - -#define kmemcheck_annotate_variable(var) \ - do { \ - } while (0) - -#endif /* CONFIG_KMEMCHECK */ - -#endif /* LINUX_KMEMCHECK_H */ diff --git a/kernel/softirq.c b/kernel/softirq.c index 662f7b1b7a78..2f5e87f1bae2 100644 --- a/kernel/softirq.c +++ b/kernel/softirq.c @@ -486,16 +486,6 @@ void __tasklet_hi_schedule(struct tasklet_struct *t) } EXPORT_SYMBOL(__tasklet_hi_schedule); -void __tasklet_hi_schedule_first(struct tasklet_struct *t) -{ - lockdep_assert_irqs_disabled(); - - t->next = __this_cpu_read(tasklet_hi_vec.head); - __this_cpu_write(tasklet_hi_vec.head, t); - __raise_softirq_irqoff(HI_SOFTIRQ); -} -EXPORT_SYMBOL(__tasklet_hi_schedule_first); - static __latent_entropy void tasklet_action(struct softirq_action *a) { struct tasklet_struct *list; diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 9576bd582d4a..7638e2f7fff8 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -1173,15 +1172,6 @@ static struct ctl_table kern_table[] = { .extra1 = &zero, .extra2 = &one_thousand, }, -#endif -#ifdef CONFIG_KMEMCHECK - { - .procname = "kmemcheck", - .data = &kmemcheck_enabled, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, #endif { .procname = "panic_on_warn", diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 07ce7449765a..5402e3954659 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -504,7 +504,7 @@ config DEBUG_OBJECTS_ENABLE_DEFAULT config DEBUG_SLAB bool "Debug slab memory allocations" - depends on DEBUG_KERNEL && SLAB && !KMEMCHECK + depends on DEBUG_KERNEL && SLAB help Say Y here to have the kernel do limited verification on memory allocation as well as poisoning memory on free to catch use of freed @@ -516,7 +516,7 @@ config DEBUG_SLAB_LEAK config SLUB_DEBUG_ON bool "SLUB debugging on by default" - depends on SLUB && SLUB_DEBUG && !KMEMCHECK + depends on SLUB && SLUB_DEBUG default n help Boot with debugging on by default. SLUB boots by default with @@ -730,8 +730,6 @@ config DEBUG_STACKOVERFLOW If in doubt, say "N". -source "lib/Kconfig.kmemcheck" - source "lib/Kconfig.kasan" endmenu # "Memory Debugging" diff --git a/lib/Kconfig.kmemcheck b/lib/Kconfig.kmemcheck deleted file mode 100644 index 846e039a86b4..000000000000 --- a/lib/Kconfig.kmemcheck +++ /dev/null @@ -1,94 +0,0 @@ -config HAVE_ARCH_KMEMCHECK - bool - -if HAVE_ARCH_KMEMCHECK - -menuconfig KMEMCHECK - bool "kmemcheck: trap use of uninitialized memory" - depends on DEBUG_KERNEL - depends on !X86_USE_3DNOW - depends on SLUB || SLAB - depends on !CC_OPTIMIZE_FOR_SIZE - depends on !FUNCTION_TRACER - select FRAME_POINTER - select STACKTRACE - default n - help - This option enables tracing of dynamically allocated kernel memory - to see if memory is used before it has been given an initial value. - Be aware that this requires half of your memory for bookkeeping and - will insert extra code at *every* read and write to tracked memory - thus slow down the kernel code (but user code is unaffected). - - The kernel may be started with kmemcheck=0 or kmemcheck=1 to disable - or enable kmemcheck at boot-time. If the kernel is started with - kmemcheck=0, the large memory and CPU overhead is not incurred. - -choice - prompt "kmemcheck: default mode at boot" - depends on KMEMCHECK - default KMEMCHECK_ONESHOT_BY_DEFAULT - help - This option controls the default behaviour of kmemcheck when the - kernel boots and no kmemcheck= parameter is given. - -config KMEMCHECK_DISABLED_BY_DEFAULT - bool "disabled" - depends on KMEMCHECK - -config KMEMCHECK_ENABLED_BY_DEFAULT - bool "enabled" - depends on KMEMCHECK - -config KMEMCHECK_ONESHOT_BY_DEFAULT - bool "one-shot" - depends on KMEMCHECK - help - In one-shot mode, only the first error detected is reported before - kmemcheck is disabled. - -endchoice - -config KMEMCHECK_QUEUE_SIZE - int "kmemcheck: error queue size" - depends on KMEMCHECK - default 64 - help - Select the maximum number of errors to store in the queue. Since - errors can occur virtually anywhere and in any context, we need a - temporary storage area which is guarantueed not to generate any - other faults. The queue will be emptied as soon as a tasklet may - be scheduled. If the queue is full, new error reports will be - lost. - -config KMEMCHECK_SHADOW_COPY_SHIFT - int "kmemcheck: shadow copy size (5 => 32 bytes, 6 => 64 bytes)" - depends on KMEMCHECK - range 2 8 - default 5 - help - Select the number of shadow bytes to save along with each entry of - the queue. These bytes indicate what parts of an allocation are - initialized, uninitialized, etc. and will be displayed when an - error is detected to help the debugging of a particular problem. - -config KMEMCHECK_PARTIAL_OK - bool "kmemcheck: allow partially uninitialized memory" - depends on KMEMCHECK - default y - help - This option works around certain GCC optimizations that produce - 32-bit reads from 16-bit variables where the upper 16 bits are - thrown away afterwards. This may of course also hide some real - bugs. - -config KMEMCHECK_BITOPS_OK - bool "kmemcheck: allow bit-field manipulation" - depends on KMEMCHECK - default n - help - This option silences warnings that would be generated for bit-field - accesses where not all the bits are initialized at the same time. - This may also hide some real bugs. - -endif diff --git a/mm/Kconfig.debug b/mm/Kconfig.debug index 5b0adf1435de..e5e606ee5f71 100644 --- a/mm/Kconfig.debug +++ b/mm/Kconfig.debug @@ -11,7 +11,6 @@ config DEBUG_PAGEALLOC bool "Debug page memory allocations" depends on DEBUG_KERNEL depends on !HIBERNATION || ARCH_SUPPORTS_DEBUG_PAGEALLOC && !PPC && !SPARC - depends on !KMEMCHECK select PAGE_EXTENSION select PAGE_POISONING if !ARCH_SUPPORTS_DEBUG_PAGEALLOC ---help--- diff --git a/mm/Makefile b/mm/Makefile index 4659b93cba43..e7ebd176fb93 100644 --- a/mm/Makefile +++ b/mm/Makefile @@ -17,7 +17,6 @@ KCOV_INSTRUMENT_slub.o := n KCOV_INSTRUMENT_page_alloc.o := n KCOV_INSTRUMENT_debug-pagealloc.o := n KCOV_INSTRUMENT_kmemleak.o := n -KCOV_INSTRUMENT_kmemcheck.o := n KCOV_INSTRUMENT_memcontrol.o := n KCOV_INSTRUMENT_mmzone.o := n KCOV_INSTRUMENT_vmstat.o := n @@ -70,7 +69,6 @@ obj-$(CONFIG_KSM) += ksm.o obj-$(CONFIG_PAGE_POISONING) += page_poison.o obj-$(CONFIG_SLAB) += slab.o obj-$(CONFIG_SLUB) += slub.o -obj-$(CONFIG_KMEMCHECK) += kmemcheck.o obj-$(CONFIG_KASAN) += kasan/ obj-$(CONFIG_FAILSLAB) += failslab.o obj-$(CONFIG_MEMORY_HOTPLUG) += memory_hotplug.o diff --git a/mm/kmemcheck.c b/mm/kmemcheck.c index b3a4d61d341c..cec594032515 100644 --- a/mm/kmemcheck.c +++ b/mm/kmemcheck.c @@ -1,126 +1 @@ // SPDX-License-Identifier: GPL-2.0 -#include -#include -#include -#include -#include "slab.h" -#include - -void kmemcheck_alloc_shadow(struct page *page, int order, gfp_t flags, int node) -{ - struct page *shadow; - int pages; - int i; - - pages = 1 << order; - - /* - * With kmemcheck enabled, we need to allocate a memory area for the - * shadow bits as well. - */ - shadow = alloc_pages_node(node, flags, order); - if (!shadow) { - if (printk_ratelimit()) - pr_err("kmemcheck: failed to allocate shadow bitmap\n"); - return; - } - - for(i = 0; i < pages; ++i) - page[i].shadow = page_address(&shadow[i]); - - /* - * Mark it as non-present for the MMU so that our accesses to - * this memory will trigger a page fault and let us analyze - * the memory accesses. - */ - kmemcheck_hide_pages(page, pages); -} - -void kmemcheck_free_shadow(struct page *page, int order) -{ - struct page *shadow; - int pages; - int i; - - if (!kmemcheck_page_is_tracked(page)) - return; - - pages = 1 << order; - - kmemcheck_show_pages(page, pages); - - shadow = virt_to_page(page[0].shadow); - - for(i = 0; i < pages; ++i) - page[i].shadow = NULL; - - __free_pages(shadow, order); -} - -void kmemcheck_slab_alloc(struct kmem_cache *s, gfp_t gfpflags, void *object, - size_t size) -{ - if (unlikely(!object)) /* Skip object if allocation failed */ - return; - - /* - * Has already been memset(), which initializes the shadow for us - * as well. - */ - if (gfpflags & __GFP_ZERO) - return; - - /* No need to initialize the shadow of a non-tracked slab. */ - if (s->flags & SLAB_NOTRACK) - return; - - if (!kmemcheck_enabled || gfpflags & __GFP_NOTRACK) { - /* - * Allow notracked objects to be allocated from - * tracked caches. Note however that these objects - * will still get page faults on access, they just - * won't ever be flagged as uninitialized. If page - * faults are not acceptable, the slab cache itself - * should be marked NOTRACK. - */ - kmemcheck_mark_initialized(object, size); - } else if (!s->ctor) { - /* - * New objects should be marked uninitialized before - * they're returned to the called. - */ - kmemcheck_mark_uninitialized(object, size); - } -} - -void kmemcheck_slab_free(struct kmem_cache *s, void *object, size_t size) -{ - /* TODO: RCU freeing is unsupported for now; hide false positives. */ - if (!s->ctor && !(s->flags & SLAB_TYPESAFE_BY_RCU)) - kmemcheck_mark_freed(object, size); -} - -void kmemcheck_pagealloc_alloc(struct page *page, unsigned int order, - gfp_t gfpflags) -{ - int pages; - - if (gfpflags & (__GFP_HIGHMEM | __GFP_NOTRACK)) - return; - - pages = 1 << order; - - /* - * NOTE: We choose to track GFP_ZERO pages too; in fact, they - * can become uninitialized by copying uninitialized memory - * into them. - */ - - /* XXX: Can use zone->node for node? */ - kmemcheck_alloc_shadow(page, order, gfpflags, -1); - - if (gfpflags & __GFP_ZERO) - kmemcheck_mark_initialized_pages(page, pages); - else - kmemcheck_mark_uninitialized_pages(page, pages); -} diff --git a/mm/slub.c b/mm/slub.c index c2c41e178acf..cfd56e5a35fb 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1371,7 +1371,7 @@ static inline void *slab_free_hook(struct kmem_cache *s, void *x) * So in order to make the debug calls that expect irqs to be * disabled we need to disable interrupts temporarily. */ -#if defined(CONFIG_KMEMCHECK) || defined(CONFIG_LOCKDEP) +#ifdef CONFIG_LOCKDEP { unsigned long flags; @@ -1399,8 +1399,7 @@ static inline void slab_free_freelist_hook(struct kmem_cache *s, * Compiler cannot detect this function can be removed if slab_free_hook() * evaluates to nothing. Thus, catch all relevant config debug options here. */ -#if defined(CONFIG_KMEMCHECK) || \ - defined(CONFIG_LOCKDEP) || \ +#if defined(CONFIG_LOCKDEP) || \ defined(CONFIG_DEBUG_KMEMLEAK) || \ defined(CONFIG_DEBUG_OBJECTS_FREE) || \ defined(CONFIG_KASAN) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index 67d051edd615..7bd52b8f63d4 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -2182,8 +2182,6 @@ sub dump_struct($$) { # strip comments: $members =~ s/\/\*.*?\*\///gos; $nested =~ s/\/\*.*?\*\///gos; - # strip kmemcheck_bitfield_{begin,end}.*; - $members =~ s/kmemcheck_bitfield_.*?;//gos; # strip attributes $members =~ s/__attribute__\s*\(\([a-z,_\*\s\(\)]*\)\)//i; $members =~ s/__aligned\s*\([^;]*\)//gos; diff --git a/tools/include/linux/kmemcheck.h b/tools/include/linux/kmemcheck.h index 2bccd2c7b897..ea32a7d3cf1b 100644 --- a/tools/include/linux/kmemcheck.h +++ b/tools/include/linux/kmemcheck.h @@ -1,9 +1 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _LIBLOCKDEP_LINUX_KMEMCHECK_H_ -#define _LIBLOCKDEP_LINUX_KMEMCHECK_H_ - -static inline void kmemcheck_mark_initialized(void *address, unsigned int n) -{ -} - -#endif -- cgit v1.3-6-gb490 From 7f855fc805cd9c29867aed56cc20f818b36a7b7b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Tue, 14 Nov 2017 18:47:20 +0900 Subject: kbuild: move coccicheck help from scripts/Makefile.help to top Makefile In my view, it is not helpful to have a separate file just for the coccicheck help message. Merge scripts/Makefile.help into the top-level Makefile. Signed-off-by: Masahiro Yamada Acked-by: Julia Lawall --- Makefile | 2 +- scripts/Makefile.help | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 scripts/Makefile.help (limited to 'scripts') diff --git a/Makefile b/Makefile index 58dd24520c9e..06a5798335fc 100644 --- a/Makefile +++ b/Makefile @@ -1384,7 +1384,7 @@ help: @echo ' export_report - List the usages of all exported symbols' @echo ' headers_check - Sanity check on exported headers' @echo ' headerdep - Detect inclusion cycles in headers' - @$(MAKE) -f $(srctree)/scripts/Makefile.help checker-help + @echo ' coccicheck - Check with Coccinelle' @echo '' @echo 'Kernel selftest:' @echo ' kselftest - Build and run kernel selftest (run as root)' diff --git a/scripts/Makefile.help b/scripts/Makefile.help deleted file mode 100644 index d03608f5db04..000000000000 --- a/scripts/Makefile.help +++ /dev/null @@ -1,3 +0,0 @@ - -checker-help: - @echo ' coccicheck - Check with Coccinelle.' -- cgit v1.3-6-gb490 From 868038bed5fb03f95ebd4517b4370b024fd13771 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 17 Nov 2017 15:26:55 -0800 Subject: spelling.txt: add "unnecessary" typo variants Add unnecessary typos by copying the necessary typos. Link: http://lkml.kernel.org/r/1505074722.22023.6.camel@perches.com Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/spelling.txt | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'scripts') diff --git a/scripts/spelling.txt b/scripts/spelling.txt index aa0cc49ad1ad..9a058cff49d4 100644 --- a/scripts/spelling.txt +++ b/scripts/spelling.txt @@ -1187,6 +1187,10 @@ unknonw||unknown unknow||unknown unkown||unknown unneded||unneeded +unneccecary||unnecessary +unneccesary||unnecessary +unneccessary||unnecessary +unnecesary||unnecessary unneedingly||unnecessarily unnsupported||unsupported unmached||unmatched -- cgit v1.3-6-gb490 From 1e6270d07cde9bfbc27f8d0f16b323cf3a3b63dd Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 17 Nov 2017 15:27:10 -0800 Subject: parse-maintainers: add ability to specify filenames parse-maintainers.pl is convenient, but currently hard-codes the filenames that are used. Allow user-specified filenames to simplify the use of the script. Link: http://lkml.kernel.org/r/48703c068b3235223ffa3b2eb268fa0a125b25e0.1502251549.git.joe@perches.com Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/parse-maintainers.pl | 52 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/parse-maintainers.pl b/scripts/parse-maintainers.pl index 5dbd2faa2449..255cef1b098d 100644 --- a/scripts/parse-maintainers.pl +++ b/scripts/parse-maintainers.pl @@ -2,9 +2,44 @@ # SPDX-License-Identifier: GPL-2.0 use strict; +use Getopt::Long qw(:config no_auto_abbrev); + +my $input_file = "MAINTAINERS"; +my $output_file = "MAINTAINERS.new"; +my $output_section = "SECTION.new"; +my $help = 0; my $P = $0; +if (!GetOptions( + 'input=s' => \$input_file, + 'output=s' => \$output_file, + 'section=s' => \$output_section, + 'h|help|usage' => \$help, + )) { + die "$P: invalid argument - use --help if necessary\n"; +} + +if ($help != 0) { + usage(); + exit 0; +} + +sub usage { + print < + + --input => MAINTAINERS file to read (default: MAINTAINERS) + --output => sorted MAINTAINERS file to write (default: MAINTAINERS.new) + --section => new sorted MAINTAINERS file to write to (default: SECTION.new) + +If exist, then the sections that match the +regexes are not written to the output file but are written to the +section file. + +EOT +} + # sort comparison functions sub by_category($$) { my ($a, $b) = @_; @@ -56,13 +91,20 @@ sub trim { sub alpha_output { my ($hashref, $filename) = (@_); + return if ! scalar(keys %$hashref); + open(my $file, '>', "$filename") or die "$P: $filename: open failed - $!\n"; + my $separator; foreach my $key (sort by_category keys %$hashref) { if ($key eq " ") { - chomp $$hashref{$key}; print $file $$hashref{$key}; } else { - print $file "\n" . $key . "\n"; + if (! defined $separator) { + $separator = "\n"; + } else { + print $file $separator; + } + print $file $key . "\n"; foreach my $pattern (sort by_pattern split('\n', %$hashref{$key})) { print $file ($pattern . "\n"); } @@ -112,7 +154,7 @@ sub file_input { my %hash; my %new_hash; -file_input(\%hash, "MAINTAINERS"); +file_input(\%hash, $input_file); foreach my $type (@ARGV) { foreach my $key (keys %hash) { @@ -123,7 +165,7 @@ foreach my $type (@ARGV) { } } -alpha_output(\%hash, "MAINTAINERS.new"); -alpha_output(\%new_hash, "SECTION.new"); +alpha_output(\%hash, $output_file); +alpha_output(\%new_hash, $output_section); exit(0); -- cgit v1.3-6-gb490 From e1f7590488541845d16afd6003d31773b9155270 Mon Sep 17 00:00:00 2001 From: Tom Saeger Date: Fri, 17 Nov 2017 15:27:42 -0800 Subject: get_maintainer: add --self-test for internal consistency tests Add "--self-test" option to get_maintainer.pl to show potential issues in MAINTAINERS file(s) content. Pattern check warnings are shown for "F" and "X" patterns found in MAINTAINERS file(s) which do not match any files known by git. Link: http://lkml.kernel.org/r/64994f911b3510d0f4c8ac2e113501dfcec1f3c9.1509559540.git.tom.saeger@oracle.com Signed-off-by: Tom Saeger Acked-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/get_maintainer.pl | 94 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 77 insertions(+), 17 deletions(-) (limited to 'scripts') diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl index bc443201d3ef..c68a5d1ba709 100755 --- a/scripts/get_maintainer.pl +++ b/scripts/get_maintainer.pl @@ -57,6 +57,7 @@ my $sections = 0; my $file_emails = 0; my $from_filename = 0; my $pattern_depth = 0; +my $self_test = 0; my $version = 0; my $help = 0; my $find_maintainer_files = 0; @@ -138,6 +139,7 @@ my %VCS_cmds_git = ( "subject_pattern" => "^GitSubject: (.*)", "stat_pattern" => "^(\\d+)\\t(\\d+)\\t\$file\$", "file_exists_cmd" => "git ls-files \$file", + "list_files_cmd" => "git ls-files \$file", ); my %VCS_cmds_hg = ( @@ -167,6 +169,7 @@ my %VCS_cmds_hg = ( "subject_pattern" => "^HgSubject: (.*)", "stat_pattern" => "^(\\d+)\t(\\d+)\t\$file\$", "file_exists_cmd" => "hg files \$file", + "list_files_cmd" => "hg manifest -R \$file", ); my $conf = which_conf(".get_maintainer.conf"); @@ -216,6 +219,14 @@ if (-f $ignore_file) { close($ignore); } +if ($#ARGV > 0) { + foreach (@ARGV) { + if ($_ eq "-self-test" || $_ eq "--self-test") { + die "$P: using --self-test does not allow any other option or argument\n"; + } + } +} + if (!GetOptions( 'email!' => \$email, 'git!' => \$email_git, @@ -252,6 +263,7 @@ if (!GetOptions( 'fe|file-emails!' => \$file_emails, 'f|file' => \$from_filename, 'find-maintainer-files' => \$find_maintainer_files, + 'self-test' => \$self_test, 'v|version' => \$version, 'h|help|usage' => \$help, )) { @@ -268,6 +280,12 @@ if ($version != 0) { exit 0; } +if ($self_test) { + read_all_maintainer_files(); + check_maintainers_patterns(); + exit 0; +} + if (-t STDIN && !@ARGV) { # We're talking to a terminal, but have no command line arguments. die "$P: missing patchfile or -f file - use --help if necessary\n"; @@ -311,12 +329,14 @@ if (!top_of_kernel_tree($lk_path)) { my @typevalue = (); my %keyword_hash; my @mfiles = (); +my @self_test_pattern_info = (); sub read_maintainer_file { my ($file) = @_; open (my $maint, '<', "$file") or die "$P: Can't open MAINTAINERS file '$file': $!\n"; + my $i = 1; while (<$maint>) { my $line = $_; @@ -333,6 +353,9 @@ sub read_maintainer_file { if ((-d $value)) { $value =~ s@([^/])$@$1/@; } + if ($self_test) { + push(@self_test_pattern_info, {file=>$file, line=>$line, linenr=>$i, pat=>$value}); + } } elsif ($type eq "K") { $keyword_hash{@typevalue} = $value; } @@ -341,6 +364,7 @@ sub read_maintainer_file { $line =~ s/\n$//g; push(@typevalue, $line); } + $i++; } close($maint); } @@ -357,26 +381,30 @@ sub find_ignore_git { return grep { $_ !~ /^\.git$/; } @_; } -if (-d "${lk_path}MAINTAINERS") { - opendir(DIR, "${lk_path}MAINTAINERS") or die $!; - my @files = readdir(DIR); - closedir(DIR); - foreach my $file (@files) { - push(@mfiles, "${lk_path}MAINTAINERS/$file") if ($file !~ /^\./); +read_all_maintainer_files(); + +sub read_all_maintainer_files { + if (-d "${lk_path}MAINTAINERS") { + opendir(DIR, "${lk_path}MAINTAINERS") or die $!; + my @files = readdir(DIR); + closedir(DIR); + foreach my $file (@files) { + push(@mfiles, "${lk_path}MAINTAINERS/$file") if ($file !~ /^\./); + } } -} -if ($find_maintainer_files) { - find( { wanted => \&find_is_maintainer_file, - preprocess => \&find_ignore_git, - no_chdir => 1, - }, "${lk_path}"); -} else { - push(@mfiles, "${lk_path}MAINTAINERS") if -f "${lk_path}MAINTAINERS"; -} + if ($find_maintainer_files) { + find( { wanted => \&find_is_maintainer_file, + preprocess => \&find_ignore_git, + no_chdir => 1, + }, "${lk_path}"); + } else { + push(@mfiles, "${lk_path}MAINTAINERS") if -f "${lk_path}MAINTAINERS"; + } -foreach my $file (@mfiles) { - read_maintainer_file("$file"); + foreach my $file (@mfiles) { + read_maintainer_file("$file"); + } } # @@ -586,6 +614,20 @@ if ($web) { exit($exit); +sub check_maintainers_patterns { + my @lsfiles = (); + + @lsfiles = vcs_list_files($lk_path); + + for my $x (@self_test_pattern_info) { + if (!grep(m@^$x->{pat}@, @lsfiles)) { + my $line = $x->{line}; + chomp($line); + print("$x->{file}:$x->{linenr}: warning: no matches $line\n"); + } + } +} + sub ignore_email_address { my ($address) = @_; @@ -863,6 +905,7 @@ Other options: --sections => print all of the subsystem sections with pattern matches --letters => print all matching 'letter' types from all matching sections --mailmap => use .mailmap file (default: $email_use_mailmap) + --self-test => show potential issues with MAINTAINERS file content --version => show version --help => show this help information @@ -2192,6 +2235,23 @@ sub vcs_file_exists { return $exists; } +sub vcs_list_files { + my ($file) = @_; + + my @lsfiles = (); + + my $vcs_used = vcs_exists(); + return 0 if (!$vcs_used); + + my $cmd = $VCS_cmds{"list_files_cmd"}; + $cmd =~ s/(\$\w+)/$1/eeg; # interpolate $cmd + @lsfiles = &{$VCS_cmds{"execute_cmd"}}($cmd); + + return () if ($? != 0); + + return @lsfiles; +} + sub uniq { my (@parms) = @_; -- cgit v1.3-6-gb490 From 083bf9c56d067559bd2727e1ec6ffb67d0ff53be Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 17 Nov 2017 15:27:46 -0800 Subject: get_maintainer: add more --self-test options Add tests for duplicate section headers, missing section content, link and scm reachability. Miscellanea: o Add --self-test= options (a comma separated list of any of sections, patterns, links or scm) where the default without options is all tests o Rename check_maintainers_patterns to self_test o Rename self_test_pattern_info to self_test_info [tom.saeger@oracle.com: improvements] Link: http://lkml.kernel.org/r/13e3986c374902fcf08ae947e36c5c608bbe3b79.1510075301.git.joe@perches.com Signed-off-by: Joe Perches Reviewed-by: Tom Saeger Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/get_maintainer.pl | 149 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 132 insertions(+), 17 deletions(-) (limited to 'scripts') diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl index c68a5d1ba709..99c96e86eccb 100755 --- a/scripts/get_maintainer.pl +++ b/scripts/get_maintainer.pl @@ -57,7 +57,7 @@ my $sections = 0; my $file_emails = 0; my $from_filename = 0; my $pattern_depth = 0; -my $self_test = 0; +my $self_test = undef; my $version = 0; my $help = 0; my $find_maintainer_files = 0; @@ -221,7 +221,7 @@ if (-f $ignore_file) { if ($#ARGV > 0) { foreach (@ARGV) { - if ($_ eq "-self-test" || $_ eq "--self-test") { + if ($_ =~ /^-{1,2}self-test(?:=|$)/) { die "$P: using --self-test does not allow any other option or argument\n"; } } @@ -263,7 +263,7 @@ if (!GetOptions( 'fe|file-emails!' => \$file_emails, 'f|file' => \$from_filename, 'find-maintainer-files' => \$find_maintainer_files, - 'self-test' => \$self_test, + 'self-test:s' => \$self_test, 'v|version' => \$version, 'h|help|usage' => \$help, )) { @@ -280,9 +280,9 @@ if ($version != 0) { exit 0; } -if ($self_test) { +if (defined $self_test) { read_all_maintainer_files(); - check_maintainers_patterns(); + self_test(); exit 0; } @@ -329,7 +329,7 @@ if (!top_of_kernel_tree($lk_path)) { my @typevalue = (); my %keyword_hash; my @mfiles = (); -my @self_test_pattern_info = (); +my @self_test_info = (); sub read_maintainer_file { my ($file) = @_; @@ -339,6 +339,7 @@ sub read_maintainer_file { my $i = 1; while (<$maint>) { my $line = $_; + chomp $line; if ($line =~ m/^([A-Z]):\s*(.*)/) { my $type = $1; @@ -353,17 +354,16 @@ sub read_maintainer_file { if ((-d $value)) { $value =~ s@([^/])$@$1/@; } - if ($self_test) { - push(@self_test_pattern_info, {file=>$file, line=>$line, linenr=>$i, pat=>$value}); - } } elsif ($type eq "K") { $keyword_hash{@typevalue} = $value; } push(@typevalue, "$type:$value"); } elsif (!(/^\s*$/ || /^\s*\#/)) { - $line =~ s/\n$//g; push(@typevalue, $line); } + if (defined $self_test) { + push(@self_test_info, {file=>$file, linenr=>$i, line=>$line}); + } $i++; } close($maint); @@ -614,17 +614,132 @@ if ($web) { exit($exit); -sub check_maintainers_patterns { +sub self_test { my @lsfiles = (); + my @good_links = (); + my @bad_links = (); + my @section_headers = (); + my $index = 0; @lsfiles = vcs_list_files($lk_path); - for my $x (@self_test_pattern_info) { - if (!grep(m@^$x->{pat}@, @lsfiles)) { - my $line = $x->{line}; - chomp($line); - print("$x->{file}:$x->{linenr}: warning: no matches $line\n"); - } + for my $x (@self_test_info) { + $index++; + + ## Section header duplication and missing section content + if (($self_test eq "" || $self_test =~ /\bsections\b/) && + $x->{line} =~ /^\S[^:]/ && + defined $self_test_info[$index] && + $self_test_info[$index]->{line} =~ /^([A-Z]):\s*\S/) { + my $has_S = 0; + my $has_F = 0; + my $has_ML = 0; + my $status = ""; + if (grep(m@^\Q$x->{line}\E@, @section_headers)) { + print("$x->{file}:$x->{linenr}: warning: duplicate section header\t$x->{line}\n"); + } else { + push(@section_headers, $x->{line}); + } + my $nextline = $index; + while (defined $self_test_info[$nextline] && + $self_test_info[$nextline]->{line} =~ /^([A-Z]):\s*(\S.*)/) { + my $type = $1; + my $value = $2; + if ($type eq "S") { + $has_S = 1; + $status = $value; + } elsif ($type eq "F" || $type eq "N") { + $has_F = 1; + } elsif ($type eq "M" || $type eq "R" || $type eq "L") { + $has_ML = 1; + } + $nextline++; + } + if (!$has_ML && $status !~ /orphan|obsolete/i) { + print("$x->{file}:$x->{linenr}: warning: section without email address\t$x->{line}\n"); + } + if (!$has_S) { + print("$x->{file}:$x->{linenr}: warning: section without status \t$x->{line}\n"); + } + if (!$has_F) { + print("$x->{file}:$x->{linenr}: warning: section without file pattern\t$x->{line}\n"); + } + } + + next if ($x->{line} !~ /^([A-Z]):\s*(.*)/); + + my $type = $1; + my $value = $2; + + ## Filename pattern matching + if (($type eq "F" || $type eq "X") && + ($self_test eq "" || $self_test =~ /\bpatterns\b/)) { + $value =~ s@\.@\\\.@g; ##Convert . to \. + $value =~ s/\*/\.\*/g; ##Convert * to .* + $value =~ s/\?/\./g; ##Convert ? to . + ##if pattern is a directory and it lacks a trailing slash, add one + if ((-d $value)) { + $value =~ s@([^/])$@$1/@; + } + if (!grep(m@^$value@, @lsfiles)) { + print("$x->{file}:$x->{linenr}: warning: no file matches\t$x->{line}\n"); + } + + ## Link reachability + } elsif (($type eq "W" || $type eq "Q" || $type eq "B") && + $value =~ /^https?:/ && + ($self_test eq "" || $self_test =~ /\blinks\b/)) { + next if (grep(m@^\Q$value\E$@, @good_links)); + my $isbad = 0; + if (grep(m@^\Q$value\E$@, @bad_links)) { + $isbad = 1; + } else { + my $output = `wget --spider -q --no-check-certificate --timeout 10 --tries 1 $value`; + if ($? == 0) { + push(@good_links, $value); + } else { + push(@bad_links, $value); + $isbad = 1; + } + } + if ($isbad) { + print("$x->{file}:$x->{linenr}: warning: possible bad link\t$x->{line}\n"); + } + + ## SCM reachability + } elsif ($type eq "T" && + ($self_test eq "" || $self_test =~ /\bscm\b/)) { + next if (grep(m@^\Q$value\E$@, @good_links)); + my $isbad = 0; + if (grep(m@^\Q$value\E$@, @bad_links)) { + $isbad = 1; + } elsif ($value !~ /^(?:git|quilt|hg)\s+\S/) { + print("$x->{file}:$x->{linenr}: warning: malformed entry\t$x->{line}\n"); + } elsif ($value =~ /^git\s+(\S+)(\s+([^\(]+\S+))?/) { + my $url = $1; + my $branch = ""; + $branch = $3 if $3; + my $output = `git ls-remote --exit-code -h "$url" $branch > /dev/null 2>&1`; + if ($? == 0) { + push(@good_links, $value); + } else { + push(@bad_links, $value); + $isbad = 1; + } + } elsif ($value =~ /^(?:quilt|hg)\s+(https?:\S+)/) { + my $url = $1; + my $output = `wget --spider -q --no-check-certificate --timeout 10 --tries 1 $url`; + if ($? == 0) { + push(@good_links, $value); + } else { + push(@bad_links, $value); + $isbad = 1; + } + } + if ($isbad) { + print("$x->{file}:$x->{linenr}: warning: possible bad link\t$x->{line}\n"); + } + } } } -- cgit v1.3-6-gb490 From 25bdda2bd68ae3008759f26058f6f9b9bb2b1cc5 Mon Sep 17 00:00:00 2001 From: Miles Chen Date: Fri, 17 Nov 2017 15:28:34 -0800 Subject: checkpatch: support function pointers for unnamed function definition arguments Current unnamed function definition argument does not include function pointer cases and it reports something like: WARNING: function definition argument 'void' should also have an identifier name +unsigned int (*dummy)(void); Support function pointers for unnamed function arguments Link: http://lkml.kernel.org/r/1505389925-31087-1-git-send-email-miles.chen@mediatek.com Signed-off-by: Miles Chen Acked-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 8b80bac055e4..aacbe918027b 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -5957,7 +5957,7 @@ sub process { # check for function declarations that have arguments without identifier names if (defined $stat && - $stat =~ /^.\s*(?:extern\s+)?$Type\s*$Ident\s*\(\s*([^{]+)\s*\)\s*;/s && + $stat =~ /^.\s*(?:extern\s+)?$Type\s*(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*\(\s*([^{]+)\s*\)\s*;/s && $1 ne "void") { my $args = trim($1); while ($args =~ m/\s*($Type\s*(?:$Ident|\(\s*\*\s*$Ident?\s*\)\s*$balanced_parens)?)/g) { -- cgit v1.3-6-gb490 From 258f79d5a1e49271f5aff38e6c1baeeaad0d82aa Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Fri, 17 Nov 2017 15:28:38 -0800 Subject: scripts/checkpatch.pl: avoid false warning missing break void foo(int a) switch (a) { case 'h': fun1(); exit(1); default: } creates a warning "Possible switch case/default not preceded by break or fallthrough comment". exit( should be treated like return. Link: http://lkml.kernel.org/r/20170910154618.25819-1-xypron.glpk@gmx.de Signed-off-by: Heinrich Schuchardt Acked-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index aacbe918027b..8dce8a8d9ed0 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -6109,7 +6109,7 @@ sub process { next if ($fline =~ /^.[\s$;]*$/); $has_statement = 1; $count++; - $has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|return\b|goto\b|continue\b)/); + $has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|exit\s*\(\b|return\b|goto\b|continue\b)/); } if (!$has_break && $has_statement) { WARN("MISSING_BREAK", -- cgit v1.3-6-gb490 From eeef5733e30e736926c762fa3336c4dd5702bcdf Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 17 Nov 2017 15:28:41 -0800 Subject: checkpatch: printks always need a KERN_ There was code in checkpatch that allowed continuation printks to be used without KERN_CONT. Remove the continuation check and always require a KERN_. Link: http://lkml.kernel.org/r/61980ef41d5b9b6543da1c49055042e0ab74d308.1507047008.git.joe@perches.com Signed-off-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) (limited to 'scripts') diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 8dce8a8d9ed0..2a8c6c3c1bdb 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3829,28 +3829,10 @@ sub process { "Prefer printk_ratelimited or pr__ratelimited to printk_ratelimit\n" . $herecurr); } -# printk should use KERN_* levels. Note that follow on printk's on the -# same line do not need a level, so we use the current block context -# to try and find and validate the current printk. In summary the current -# printk includes all preceding printk's which have no newline on the end. -# we assume the first bad printk is the one to report. - if ($line =~ /\bprintk\((?!KERN_)\s*"/) { - my $ok = 0; - for (my $ln = $linenr - 1; $ln >= $first_line; $ln--) { - #print "CHECK<$lines[$ln - 1]\n"; - # we have a preceding printk if it ends - # with "\n" ignore it, else it is to blame - if ($lines[$ln - 1] =~ m{\bprintk\(}) { - if ($rawlines[$ln - 1] !~ m{\\n"}) { - $ok = 1; - } - last; - } - } - if ($ok == 0) { - WARN("PRINTK_WITHOUT_KERN_LEVEL", - "printk() should include KERN_ facility level\n" . $herecurr); - } +# printk should use KERN_* levels + if ($line =~ /\bprintk\s*\(\s*(?!KERN_[A-Z]+\b)/) { + WARN("PRINTK_WITHOUT_KERN_LEVEL", + "printk() should include KERN_ facility level\n" . $herecurr); } if ($line =~ /\bprintk\s*\(\s*KERN_([A-Z]+)/) { -- cgit v1.3-6-gb490 From cc147506bef96f31af799afe94d0c0e81161ee6c Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 17 Nov 2017 15:28:44 -0800 Subject: checkpatch: allow DEFINE_PER_CPU definitions to exceed line length Some of the definitions are very long and can't be split into multiple lines because ctags is limited. Exempt these lines from the line length checks. See commit 25528213fe9f ("tags: Fix DEFINE_PER_CPU expansions") for more details. Link: http://lkml.kernel.org/r/1508170320.6530.15.camel@perches.com Signed-off-by: Joe Perches Acked-by: Mark Rutland Cc: Daniel Lezcano Cc: Marc Zyngier Cc: Thomas Gleixner Cc: Ard Biesheuvel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 2a8c6c3c1bdb..5fa0f5467d99 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2900,8 +2900,9 @@ sub process { $line =~ /^\+\s*#\s*define\s+\w+\s+$String$/) { $msg_type = ""; - # EFI_GUID is another special case - } elsif ($line =~ /^\+.*\bEFI_GUID\s*\(/) { + # More special cases + } elsif ($line =~ /^\+.*\bEFI_GUID\s*\(/ || + $line =~ /^\+\s*(?:\w+)?\s*DEFINE_PER_CPU/) { $msg_type = ""; # Otherwise set the alternate message types -- cgit v1.3-6-gb490 From 87bd499af5cd663c150032cca4bac822a729263b Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 17 Nov 2017 15:28:48 -0800 Subject: checkpatch: add TP_printk to list of logging functions So the line length check can be bypassed by its callers. Link: http://lkml.kernel.org/r/7de542c08a6e79f2ebe7c1416c9f403c23fdcc09.1508282823.git.joe@perches.com Signed-off-by: Joe Perches Reported-by: Song Liu Tested-by: Song Liu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 1 + 1 file changed, 1 insertion(+) (limited to 'scripts') diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 5fa0f5467d99..6bdd43d5dec5 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -454,6 +454,7 @@ our $zero_initializer = qr{(?:(?:0[xX])?0+$Int_type?|NULL|false)\b}; our $logFunctions = qr{(?x: printk(?:_ratelimited|_once|_deferred_once|_deferred|)| (?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)| + TP_printk| WARN(?:_RATELIMIT|_ONCE|)| panic| MODULE_[A-Z_]+| -- cgit v1.3-6-gb490 From 5751a24edfd43a91e072d63cde2b99b5a421645f Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 17 Nov 2017 15:28:52 -0800 Subject: checkpatch: add --strict test for lines ending in [ or ( Lines that end in an open bracket or open parenthesis are generally hard to follow. Lines following those ending with open parenthesis are also rarely aligned to that open parenthesis. Suggest not ending lines with '[' or '(' Link: http://lkml.kernel.org/r/8fd0b2b4a7482064254e37931eb9302a81d5aa2f.1508340786.git.joe@perches.com Signed-off-by: Joe Perches Suggested-by: Vivien Didelot Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'scripts') diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 6bdd43d5dec5..3453df9f90ab 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3184,6 +3184,12 @@ sub process { # check we are in a valid C source file if not then ignore this hunk next if ($realfile !~ /\.(h|c)$/); +# check for unusual line ending [ or ( + if ($line =~ /^\+.*([\[\(])\s*$/) { + CHK("OPEN_ENDED_LINE", + "Lines should not end with a '$1'\n" . $herecurr); + } + # check if this appears to be the start function declaration, save the name if ($sline =~ /^\+\{\s*$/ && $prevline =~ /^\+(?:(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*)?($Ident)\(/) { -- cgit v1.3-6-gb490 From 0bc989ffc802752c5256192b4a9c8f16a00feca7 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Fri, 17 Nov 2017 15:28:55 -0800 Subject: checkpatch: do not check missing blank line before builtin_*_driver checkpatch.pl does not check missing blank line before module_*_driver. I want it to behave likewise for builtin_*_driver. Link: http://lkml.kernel.org/r/1505700081-12854-1-git-send-email-yamada.masahiro@socionext.com Signed-off-by: Masahiro Yamada Acked-by: Joe Perches Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- scripts/checkpatch.pl | 1 + 1 file changed, 1 insertion(+) (limited to 'scripts') diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 3453df9f90ab..95cda3ecc66b 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3105,6 +3105,7 @@ sub process { $line =~ /^\+[a-z_]*init/ || $line =~ /^\+\s*(?:static\s+)?[A-Z_]*ATTR/ || $line =~ /^\+\s*DECLARE/ || + $line =~ /^\+\s*builtin_[\w_]*driver/ || $line =~ /^\+\s*__setup/)) { if (CHK("LINE_SPACING", "Please use a blank line after function/struct/union/enum declarations\n" . $hereprev) && -- cgit v1.3-6-gb490 From d677a4d6019385488e794cc47bd3d6f9c2aab874 Mon Sep 17 00:00:00 2001 From: Victor Chibotaru Date: Fri, 17 Nov 2017 15:30:50 -0800 Subject: Makefile: support flag -fsanitizer-coverage=trace-cmp The flag enables Clang instrumentation of comparison operations (currently not supported by GCC). This instrumentation is needed by the new KCOV device to collect comparison operands. Link: http://lkml.kernel.org/r/20171011095459.70721-2-glider@google.com Signed-off-by: Victor Chibotaru Signed-off-by: Alexander Potapenko Cc: Dmitry Vyukov Cc: Andrey Konovalov Cc: Mark Rutland Cc: Alexander Popov Cc: Andrey Ryabinin Cc: Kees Cook Cc: Vegard Nossum Cc: Quentin Casasnovas Cc: Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Makefile | 3 +-- lib/Kconfig.debug | 10 ++++++++++ scripts/Makefile.kcov | 7 +++++++ 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 scripts/Makefile.kcov (limited to 'scripts') diff --git a/Makefile b/Makefile index 763ab35df12a..ccb7d5b2fbf5 100644 --- a/Makefile +++ b/Makefile @@ -375,8 +375,6 @@ CFLAGS_KERNEL = AFLAGS_KERNEL = LDFLAGS_vmlinux = CFLAGS_GCOV := -fprofile-arcs -ftest-coverage -fno-tree-loop-im $(call cc-disable-warning,maybe-uninitialized,) -CFLAGS_KCOV := $(call cc-option,-fsanitize-coverage=trace-pc,) - # Use USERINCLUDE when you must reference the UAPI directories only. USERINCLUDE := \ @@ -659,6 +657,7 @@ ifeq ($(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-goto.sh $(CC) $(KBUILD_CFLA KBUILD_AFLAGS += -DCC_HAVE_ASM_GOTO endif +include scripts/Makefile.kcov include scripts/Makefile.gcc-plugins ifdef CONFIG_READABLE_ASM diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index ce813731269f..947d3e2ed5c2 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -756,6 +756,16 @@ config KCOV For more details, see Documentation/dev-tools/kcov.rst. +config KCOV_ENABLE_COMPARISONS + bool "Enable comparison operands collection by KCOV" + depends on KCOV + default n + help + KCOV also exposes operands of every comparison in the instrumented + code along with operand sizes and PCs of the comparison instructions. + These operands can be used by fuzzing engines to improve the quality + of fuzzing coverage. + config KCOV_INSTRUMENT_ALL bool "Instrument all code by default" depends on KCOV diff --git a/scripts/Makefile.kcov b/scripts/Makefile.kcov new file mode 100644 index 000000000000..5cc72037e423 --- /dev/null +++ b/scripts/Makefile.kcov @@ -0,0 +1,7 @@ +ifdef CONFIG_KCOV +CFLAGS_KCOV := $(call cc-option,-fsanitize-coverage=trace-pc,) +ifeq ($(CONFIG_KCOV_ENABLE_COMPARISONS),y) +CFLAGS_KCOV += $(call cc-option,-fsanitize-coverage=trace-cmp,) +endif + +endif -- cgit v1.3-6-gb490 From 9477b4ad7019ad423cc88a6b83fa717a5d8d9857 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 26 Oct 2017 07:31:17 -0700 Subject: Coccinelle: Remove setup_timer.cocci Both the init_timer() and timer_setup() APIs have been removed. This script will not be needed any more. Signed-off-by: Kees Cook --- scripts/coccinelle/api/setup_timer.cocci | 277 ------------------------------- 1 file changed, 277 deletions(-) delete mode 100644 scripts/coccinelle/api/setup_timer.cocci (limited to 'scripts') diff --git a/scripts/coccinelle/api/setup_timer.cocci b/scripts/coccinelle/api/setup_timer.cocci deleted file mode 100644 index e4577089dcb9..000000000000 --- a/scripts/coccinelle/api/setup_timer.cocci +++ /dev/null @@ -1,277 +0,0 @@ -/// Use setup_timer function instead of initializing timer with the function -/// and data fields -// Confidence: High -// Copyright: (C) 2016 Vaishali Thakkar, Oracle. GPLv2 -// Copyright: (C) 2017 Kees Cook, Google. GPLv2 -// Options: --no-includes --include-headers -// Keywords: init_timer, setup_timer - -virtual patch -virtual context -virtual org -virtual report - -// Match the common cases first to avoid Coccinelle parsing loops with -// "... when" clauses. - -@match_immediate_function_data_after_init_timer -depends on patch && !context && !org && !report@ -expression e, func, da; -@@ - --init_timer -+setup_timer - ( \(&e\|e\) -+, func, da - ); -( --\(e.function\|e->function\) = func; --\(e.data\|e->data\) = da; -| --\(e.data\|e->data\) = da; --\(e.function\|e->function\) = func; -) - -@match_immediate_function_data_before_init_timer -depends on patch && !context && !org && !report@ -expression e, func, da; -@@ - -( --\(e.function\|e->function\) = func; --\(e.data\|e->data\) = da; -| --\(e.data\|e->data\) = da; --\(e.function\|e->function\) = func; -) --init_timer -+setup_timer - ( \(&e\|e\) -+, func, da - ); - -@match_function_and_data_after_init_timer -depends on patch && !context && !org && !report@ -expression e, e2, e3, e4, e5, func, da; -@@ - --init_timer -+setup_timer - ( \(&e\|e\) -+, func, da - ); - ... when != func = e2 - when != da = e3 -( --e.function = func; -... when != da = e4 --e.data = da; -| --e->function = func; -... when != da = e4 --e->data = da; -| --e.data = da; -... when != func = e5 --e.function = func; -| --e->data = da; -... when != func = e5 --e->function = func; -) - -@match_function_and_data_before_init_timer -depends on patch && !context && !org && !report@ -expression e, e2, e3, e4, e5, func, da; -@@ -( --e.function = func; -... when != da = e4 --e.data = da; -| --e->function = func; -... when != da = e4 --e->data = da; -| --e.data = da; -... when != func = e5 --e.function = func; -| --e->data = da; -... when != func = e5 --e->function = func; -) -... when != func = e2 - when != da = e3 --init_timer -+setup_timer - ( \(&e\|e\) -+, func, da - ); - -@r1 exists@ -expression t; -identifier f; -position p; -@@ - -f(...) { ... when any - init_timer@p(\(&t\|t\)) - ... when any -} - -@r2 exists@ -expression r1.t; -identifier g != r1.f; -expression e8; -@@ - -g(...) { ... when any - \(t.data\|t->data\) = e8 - ... when any -} - -// It is dangerous to use setup_timer if data field is initialized -// in another function. - -@script:python depends on r2@ -p << r1.p; -@@ - -cocci.include_match(False) - -@r3 depends on patch && !context && !org && !report@ -expression r1.t, func, e7; -position r1.p; -@@ - -( --init_timer@p(&t); -+setup_timer(&t, func, 0UL); -... when != func = e7 --t.function = func; -| --t.function = func; -... when != func = e7 --init_timer@p(&t); -+setup_timer(&t, func, 0UL); -| --init_timer@p(t); -+setup_timer(t, func, 0UL); -... when != func = e7 --t->function = func; -| --t->function = func; -... when != func = e7 --init_timer@p(t); -+setup_timer(t, func, 0UL); -) - -// ---------------------------------------------------------------------------- - -@match_immediate_function_data_after_init_timer_context -depends on !patch && (context || org || report)@ -expression da, e, func; -position j0, j1, j2; -@@ - -* init_timer@j0 (&e); -( -* e@j1.function = func; -* e@j2.data = da; -| -* e@j1.data = da; -* e@j2.function = func; -) - -@match_function_and_data_after_init_timer_context -depends on !patch && (context || org || report)@ -expression a, b, e1, e2, e3, e4, e5; -position j0 != match_immediate_function_data_after_init_timer_context.j0,j1,j2; -@@ - -* init_timer@j0 (&e1); -... when != a = e2 - when != b = e3 -( -* e1@j1.function = a; -... when != b = e4 -* e1@j2.data = b; -| -* e1@j1.data = b; -... when != a = e5 -* e1@j2.function = a; -) - -@r3_context depends on !patch && (context || org || report)@ -expression c, e6, e7; -position r1.p; -position j0 != - {match_immediate_function_data_after_init_timer_context.j0, - match_function_and_data_after_init_timer_context.j0}, j1; -@@ - -* init_timer@j0@p (&e6); -... when != c = e7 -* e6@j1.function = c; - -// ---------------------------------------------------------------------------- - -@script:python match_immediate_function_data_after_init_timer_org -depends on org@ -j0 << match_immediate_function_data_after_init_timer_context.j0; -j1 << match_immediate_function_data_after_init_timer_context.j1; -j2 << match_immediate_function_data_after_init_timer_context.j2; -@@ - -msg = "Use setup_timer function." -coccilib.org.print_todo(j0[0], msg) -coccilib.org.print_link(j1[0], "") -coccilib.org.print_link(j2[0], "") - -@script:python match_function_and_data_after_init_timer_org depends on org@ -j0 << match_function_and_data_after_init_timer_context.j0; -j1 << match_function_and_data_after_init_timer_context.j1; -j2 << match_function_and_data_after_init_timer_context.j2; -@@ - -msg = "Use setup_timer function." -coccilib.org.print_todo(j0[0], msg) -coccilib.org.print_link(j1[0], "") -coccilib.org.print_link(j2[0], "") - -@script:python r3_org depends on org@ -j0 << r3_context.j0; -j1 << r3_context.j1; -@@ - -msg = "Use setup_timer function." -coccilib.org.print_todo(j0[0], msg) -coccilib.org.print_link(j1[0], "") - -// ---------------------------------------------------------------------------- - -@script:python match_immediate_function_data_after_init_timer_report -depends on report@ -j0 << match_immediate_function_data_after_init_timer_context.j0; -j1 << match_immediate_function_data_after_init_timer_context.j1; -@@ - -msg = "Use setup_timer function for function on line %s." % (j1[0].line) -coccilib.report.print_report(j0[0], msg) - -@script:python match_function_and_data_after_init_timer_report depends on report@ -j0 << match_function_and_data_after_init_timer_context.j0; -j1 << match_function_and_data_after_init_timer_context.j1; -@@ - -msg = "Use setup_timer function for function on line %s." % (j1[0].line) -coccilib.report.print_report(j0[0], msg) - -@script:python r3_report depends on report@ -j0 << r3_context.j0; -j1 << r3_context.j1; -@@ - -msg = "Use setup_timer function for function on line %s." % (j1[0].line) -coccilib.report.print_report(j0[0], msg) -- cgit v1.3-6-gb490