diff options
Diffstat (limited to 'tools/testing')
85 files changed, 2669 insertions, 167 deletions
diff --git a/tools/testing/nvdimm/Kbuild b/tools/testing/nvdimm/Kbuild index 65368d9027f5..db33b28c5ef3 100644 --- a/tools/testing/nvdimm/Kbuild +++ b/tools/testing/nvdimm/Kbuild @@ -70,6 +70,7 @@ libnvdimm-y += $(NVDIMM_SRC)/region_devs.o libnvdimm-y += $(NVDIMM_SRC)/region.o libnvdimm-y += $(NVDIMM_SRC)/namespace_devs.o libnvdimm-y += $(NVDIMM_SRC)/label.o +libnvdimm-y += $(NVDIMM_SRC)/badrange.o libnvdimm-$(CONFIG_ND_CLAIM) += $(NVDIMM_SRC)/claim.o libnvdimm-$(CONFIG_BTT) += $(NVDIMM_SRC)/btt_devs.o libnvdimm-$(CONFIG_NVDIMM_PFN) += $(NVDIMM_SRC)/pfn_devs.o diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c index bef419d4266d..7217b2b953b5 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c @@ -168,8 +168,12 @@ struct nfit_test { spinlock_t lock; } ars_state; struct device *dimm_dev[NUM_DCR]; + struct badrange badrange; + struct work_struct work; }; +static struct workqueue_struct *nfit_wq; + static struct nfit_test *to_nfit_test(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -234,48 +238,68 @@ static int nfit_test_cmd_set_config_data(struct nd_cmd_set_config_hdr *nd_cmd, return rc; } -#define NFIT_TEST_ARS_RECORDS 4 #define NFIT_TEST_CLEAR_ERR_UNIT 256 static int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd, unsigned int buf_len) { + int ars_recs; + if (buf_len < sizeof(*nd_cmd)) return -EINVAL; + /* for testing, only store up to n records that fit within 4k */ + ars_recs = SZ_4K / sizeof(struct nd_ars_record); + nd_cmd->max_ars_out = sizeof(struct nd_cmd_ars_status) - + NFIT_TEST_ARS_RECORDS * sizeof(struct nd_ars_record); + + ars_recs * sizeof(struct nd_ars_record); nd_cmd->status = (ND_ARS_PERSISTENT | ND_ARS_VOLATILE) << 16; nd_cmd->clear_err_unit = NFIT_TEST_CLEAR_ERR_UNIT; return 0; } -/* - * Initialize the ars_state to return an ars_result 1 second in the future with - * a 4K error range in the middle of the requested address range. - */ -static void post_ars_status(struct ars_state *ars_state, u64 addr, u64 len) +static void post_ars_status(struct ars_state *ars_state, + struct badrange *badrange, u64 addr, u64 len) { struct nd_cmd_ars_status *ars_status; struct nd_ars_record *ars_record; + struct badrange_entry *be; + u64 end = addr + len - 1; + int i = 0; ars_state->deadline = jiffies + 1*HZ; ars_status = ars_state->ars_status; ars_status->status = 0; - ars_status->out_length = sizeof(struct nd_cmd_ars_status) - + sizeof(struct nd_ars_record); ars_status->address = addr; ars_status->length = len; ars_status->type = ND_ARS_PERSISTENT; - ars_status->num_records = 1; - ars_record = &ars_status->records[0]; - ars_record->handle = 0; - ars_record->err_address = addr + len / 2; - ars_record->length = SZ_4K; + + spin_lock(&badrange->lock); + list_for_each_entry(be, &badrange->list, list) { + u64 be_end = be->start + be->length - 1; + u64 rstart, rend; + + /* skip entries outside the range */ + if (be_end < addr || be->start > end) + continue; + + rstart = (be->start < addr) ? addr : be->start; + rend = (be_end < end) ? be_end : end; + ars_record = &ars_status->records[i]; + ars_record->handle = 0; + ars_record->err_address = rstart; + ars_record->length = rend - rstart + 1; + i++; + } + spin_unlock(&badrange->lock); + ars_status->num_records = i; + ars_status->out_length = sizeof(struct nd_cmd_ars_status) + + i * sizeof(struct nd_ars_record); } -static int nfit_test_cmd_ars_start(struct ars_state *ars_state, +static int nfit_test_cmd_ars_start(struct nfit_test *t, + struct ars_state *ars_state, struct nd_cmd_ars_start *ars_start, unsigned int buf_len, int *cmd_rc) { @@ -289,7 +313,7 @@ static int nfit_test_cmd_ars_start(struct ars_state *ars_state, } else { ars_start->status = 0; ars_start->scrub_time = 1; - post_ars_status(ars_state, ars_start->address, + post_ars_status(ars_state, &t->badrange, ars_start->address, ars_start->length); *cmd_rc = 0; } @@ -320,7 +344,8 @@ static int nfit_test_cmd_ars_status(struct ars_state *ars_state, return 0; } -static int nfit_test_cmd_clear_error(struct nd_cmd_clear_error *clear_err, +static int nfit_test_cmd_clear_error(struct nfit_test *t, + struct nd_cmd_clear_error *clear_err, unsigned int buf_len, int *cmd_rc) { const u64 mask = NFIT_TEST_CLEAR_ERR_UNIT - 1; @@ -330,18 +355,91 @@ static int nfit_test_cmd_clear_error(struct nd_cmd_clear_error *clear_err, if ((clear_err->address & mask) || (clear_err->length & mask)) return -EINVAL; - /* - * Report 'all clear' success for all commands even though a new - * scrub will find errors again. This is enough to have the - * error removed from the 'badblocks' tracking in the pmem - * driver. - */ + badrange_forget(&t->badrange, clear_err->address, clear_err->length); clear_err->status = 0; clear_err->cleared = clear_err->length; *cmd_rc = 0; return 0; } +struct region_search_spa { + u64 addr; + struct nd_region *region; +}; + +static int is_region_device(struct device *dev) +{ + return !strncmp(dev->kobj.name, "region", 6); +} + +static int nfit_test_search_region_spa(struct device *dev, void *data) +{ + struct region_search_spa *ctx = data; + struct nd_region *nd_region; + resource_size_t ndr_end; + + if (!is_region_device(dev)) + return 0; + + nd_region = to_nd_region(dev); + ndr_end = nd_region->ndr_start + nd_region->ndr_size; + + if (ctx->addr >= nd_region->ndr_start && ctx->addr < ndr_end) { + ctx->region = nd_region; + return 1; + } + + return 0; +} + +static int nfit_test_search_spa(struct nvdimm_bus *bus, + struct nd_cmd_translate_spa *spa) +{ + int ret; + struct nd_region *nd_region = NULL; + struct nvdimm *nvdimm = NULL; + struct nd_mapping *nd_mapping = NULL; + struct region_search_spa ctx = { + .addr = spa->spa, + .region = NULL, + }; + u64 dpa; + + ret = device_for_each_child(&bus->dev, &ctx, + nfit_test_search_region_spa); + + if (!ret) + return -ENODEV; + + nd_region = ctx.region; + + dpa = ctx.addr - nd_region->ndr_start; + + /* + * last dimm is selected for test + */ + nd_mapping = &nd_region->mapping[nd_region->ndr_mappings - 1]; + nvdimm = nd_mapping->nvdimm; + + spa->devices[0].nfit_device_handle = handle[nvdimm->id]; + spa->num_nvdimms = 1; + spa->devices[0].dpa = dpa; + + return 0; +} + +static int nfit_test_cmd_translate_spa(struct nvdimm_bus *bus, + struct nd_cmd_translate_spa *spa, unsigned int buf_len) +{ + if (buf_len < spa->translate_length) + return -EINVAL; + + if (nfit_test_search_spa(bus, spa) < 0 || !spa->num_nvdimms) + spa->status = 2; + + return 0; +} + static int nfit_test_cmd_smart(struct nd_cmd_smart *smart, unsigned int buf_len) { static const struct nd_smart_payload smart_data = { @@ -378,6 +476,93 @@ static int nfit_test_cmd_smart_threshold(struct nd_cmd_smart_threshold *smart_t, return 0; } +static void uc_error_notify(struct work_struct *work) +{ + struct nfit_test *t = container_of(work, typeof(*t), work); + + __acpi_nfit_notify(&t->pdev.dev, t, NFIT_NOTIFY_UC_MEMORY_ERROR); +} + +static int nfit_test_cmd_ars_error_inject(struct nfit_test *t, + struct nd_cmd_ars_err_inj *err_inj, unsigned int buf_len) +{ + int rc; + + if (buf_len != sizeof(*err_inj)) { + rc = -EINVAL; + goto err; + } + + if (err_inj->err_inj_spa_range_length <= 0) { + rc = -EINVAL; + goto err; + } + + rc = badrange_add(&t->badrange, err_inj->err_inj_spa_range_base, + err_inj->err_inj_spa_range_length); + if (rc < 0) + goto err; + + if (err_inj->err_inj_options & (1 << ND_ARS_ERR_INJ_OPT_NOTIFY)) + queue_work(nfit_wq, &t->work); + + err_inj->status = 0; + return 0; + +err: + err_inj->status = NFIT_ARS_INJECT_INVALID; + return rc; +} + +static int nfit_test_cmd_ars_inject_clear(struct nfit_test *t, + struct nd_cmd_ars_err_inj_clr *err_clr, unsigned int buf_len) +{ + int rc; + + if (buf_len != sizeof(*err_clr)) { + rc = -EINVAL; + goto err; + } + + if (err_clr->err_inj_clr_spa_range_length <= 0) { + rc = -EINVAL; + goto err; + } + + badrange_forget(&t->badrange, err_clr->err_inj_clr_spa_range_base, + err_clr->err_inj_clr_spa_range_length); + + err_clr->status = 0; + return 0; + +err: + err_clr->status = NFIT_ARS_INJECT_INVALID; + return rc; +} + +static int nfit_test_cmd_ars_inject_status(struct nfit_test *t, + struct nd_cmd_ars_err_inj_stat *err_stat, + unsigned int buf_len) +{ + struct badrange_entry *be; + int max = SZ_4K / sizeof(struct nd_error_stat_query_record); + int i = 0; + + err_stat->status = 0; + spin_lock(&t->badrange.lock); + list_for_each_entry(be, &t->badrange.list, list) { + err_stat->record[i].err_inj_stat_spa_range_base = be->start; + err_stat->record[i].err_inj_stat_spa_range_length = be->length; + i++; + if (i > max) + break; + } + spin_unlock(&t->badrange.lock); + err_stat->inj_err_rec_count = i; + + return 0; +} + static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc) @@ -449,6 +634,38 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, } } else { struct ars_state *ars_state = &t->ars_state; + struct nd_cmd_pkg *call_pkg = buf; + + if (!nd_desc) + return -ENOTTY; + + if (cmd == ND_CMD_CALL) { + func = call_pkg->nd_command; + + buf_len = call_pkg->nd_size_in + call_pkg->nd_size_out; + buf = (void *) call_pkg->nd_payload; + + switch (func) { + case NFIT_CMD_TRANSLATE_SPA: + rc = nfit_test_cmd_translate_spa( + acpi_desc->nvdimm_bus, buf, buf_len); + return rc; + case NFIT_CMD_ARS_INJECT_SET: + rc = nfit_test_cmd_ars_error_inject(t, buf, + buf_len); + return rc; + case NFIT_CMD_ARS_INJECT_CLEAR: + rc = nfit_test_cmd_ars_inject_clear(t, buf, + buf_len); + return rc; + case NFIT_CMD_ARS_INJECT_GET: + rc = nfit_test_cmd_ars_inject_status(t, buf, + buf_len); + return rc; + default: + return -ENOTTY; + } + } if (!nd_desc || !test_bit(cmd, &nd_desc->cmd_mask)) return -ENOTTY; @@ -458,15 +675,15 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, rc = nfit_test_cmd_ars_cap(buf, buf_len); break; case ND_CMD_ARS_START: - rc = nfit_test_cmd_ars_start(ars_state, buf, buf_len, - cmd_rc); + rc = nfit_test_cmd_ars_start(t, ars_state, buf, + buf_len, cmd_rc); break; case ND_CMD_ARS_STATUS: rc = nfit_test_cmd_ars_status(ars_state, buf, buf_len, cmd_rc); break; case ND_CMD_CLEAR_ERROR: - rc = nfit_test_cmd_clear_error(buf, buf_len, cmd_rc); + rc = nfit_test_cmd_clear_error(t, buf, buf_len, cmd_rc); break; default: return -ENOTTY; @@ -566,10 +783,9 @@ static struct nfit_test_resource *nfit_test_lookup(resource_size_t addr) static int ars_state_init(struct device *dev, struct ars_state *ars_state) { + /* for testing, only store up to n records that fit within 4k */ ars_state->ars_status = devm_kzalloc(dev, - sizeof(struct nd_cmd_ars_status) - + sizeof(struct nd_ars_record) * NFIT_TEST_ARS_RECORDS, - GFP_KERNEL); + sizeof(struct nd_cmd_ars_status) + SZ_4K, GFP_KERNEL); if (!ars_state->ars_status) return -ENOMEM; spin_lock_init(&ars_state->lock); @@ -1419,7 +1635,8 @@ static void nfit_test0_setup(struct nfit_test *t) + i * sizeof(u64); } - post_ars_status(&t->ars_state, t->spa_set_dma[0], SPA0_SIZE); + post_ars_status(&t->ars_state, &t->badrange, t->spa_set_dma[0], + SPA0_SIZE); acpi_desc = &t->acpi_desc; set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_cmd_force_en); @@ -1430,7 +1647,12 @@ static void nfit_test0_setup(struct nfit_test *t) set_bit(ND_CMD_ARS_START, &acpi_desc->bus_cmd_force_en); set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_cmd_force_en); set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_cmd_force_en); + set_bit(ND_CMD_CALL, &acpi_desc->bus_cmd_force_en); set_bit(ND_CMD_SMART_THRESHOLD, &acpi_desc->dimm_cmd_force_en); + set_bit(NFIT_CMD_TRANSLATE_SPA, &acpi_desc->bus_nfit_cmd_force_en); + set_bit(NFIT_CMD_ARS_INJECT_SET, &acpi_desc->bus_nfit_cmd_force_en); + set_bit(NFIT_CMD_ARS_INJECT_CLEAR, &acpi_desc->bus_nfit_cmd_force_en); + set_bit(NFIT_CMD_ARS_INJECT_GET, &acpi_desc->bus_nfit_cmd_force_en); } static void nfit_test1_setup(struct nfit_test *t) @@ -1520,7 +1742,8 @@ static void nfit_test1_setup(struct nfit_test *t) dcr->code = NFIT_FIC_BYTE; dcr->windows = 0; - post_ars_status(&t->ars_state, t->spa_set_dma[0], SPA2_SIZE); + post_ars_status(&t->ars_state, &t->badrange, t->spa_set_dma[0], + SPA2_SIZE); acpi_desc = &t->acpi_desc; set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_cmd_force_en); @@ -1589,6 +1812,7 @@ static int nfit_ctl_test(struct device *dev) unsigned long mask, cmd_size, offset; union { struct nd_cmd_get_config_size cfg_size; + struct nd_cmd_clear_error clear_err; struct nd_cmd_ars_status ars_stat; struct nd_cmd_ars_cap ars_cap; char buf[sizeof(struct nd_cmd_ars_status) @@ -1613,10 +1837,15 @@ static int nfit_ctl_test(struct device *dev) .cmd_mask = 1UL << ND_CMD_ARS_CAP | 1UL << ND_CMD_ARS_START | 1UL << ND_CMD_ARS_STATUS - | 1UL << ND_CMD_CLEAR_ERROR, + | 1UL << ND_CMD_CLEAR_ERROR + | 1UL << ND_CMD_CALL, .module = THIS_MODULE, .provider_name = "ACPI.NFIT", .ndctl = acpi_nfit_ctl, + .bus_dsm_mask = 1UL << NFIT_CMD_TRANSLATE_SPA + | 1UL << NFIT_CMD_ARS_INJECT_SET + | 1UL << NFIT_CMD_ARS_INJECT_CLEAR + | 1UL << NFIT_CMD_ARS_INJECT_GET, }, .dev = &adev->dev, }; @@ -1767,6 +1996,23 @@ static int nfit_ctl_test(struct device *dev) return -EIO; } + /* test clear error */ + cmd_size = sizeof(cmds.clear_err); + cmds.clear_err = (struct nd_cmd_clear_error) { + .length = 512, + .cleared = 512, + }; + rc = setup_result(cmds.buf, cmd_size); + if (rc) + return rc; + rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_CLEAR_ERROR, + cmds.buf, cmd_size, &cmd_rc); + if (rc < 0 || cmd_rc) { + dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n", + __func__, __LINE__, rc, cmd_rc); + return -EIO; + } + return 0; } @@ -1915,6 +2161,10 @@ static __init int nfit_test_init(void) nfit_test_setup(nfit_test_lookup, nfit_test_evaluate_dsm); + nfit_wq = create_singlethread_workqueue("nfit"); + if (!nfit_wq) + return -ENOMEM; + nfit_test_dimm = class_create(THIS_MODULE, "nfit_test_dimm"); if (IS_ERR(nfit_test_dimm)) { rc = PTR_ERR(nfit_test_dimm); @@ -1931,6 +2181,7 @@ static __init int nfit_test_init(void) goto err_register; } INIT_LIST_HEAD(&nfit_test->resources); + badrange_init(&nfit_test->badrange); switch (i) { case 0: nfit_test->num_pm = NUM_PM; @@ -1966,6 +2217,7 @@ static __init int nfit_test_init(void) goto err_register; instances[i] = nfit_test; + INIT_WORK(&nfit_test->work, uc_error_notify); } rc = platform_driver_register(&nfit_test_driver); @@ -1974,6 +2226,7 @@ static __init int nfit_test_init(void) return 0; err_register: + destroy_workqueue(nfit_wq); for (i = 0; i < NUM_NFITS; i++) if (instances[i]) platform_device_unregister(&instances[i]->pdev); @@ -1989,6 +2242,8 @@ static __exit void nfit_test_exit(void) { int i; + flush_workqueue(nfit_wq); + destroy_workqueue(nfit_wq); for (i = 0; i < NUM_NFITS; i++) platform_device_unregister(&instances[i]->pdev); platform_driver_unregister(&nfit_test_driver); diff --git a/tools/testing/nvdimm/test/nfit_test.h b/tools/testing/nvdimm/test/nfit_test.h index d3d63dd5ed38..113b44675a71 100644 --- a/tools/testing/nvdimm/test/nfit_test.h +++ b/tools/testing/nvdimm/test/nfit_test.h @@ -32,6 +32,58 @@ struct nfit_test_resource { void *buf; }; +#define ND_TRANSLATE_SPA_STATUS_INVALID_SPA 2 +#define NFIT_ARS_INJECT_INVALID 2 + +enum err_inj_options { + ND_ARS_ERR_INJ_OPT_NOTIFY = 0, +}; + +/* nfit commands */ +enum nfit_cmd_num { + NFIT_CMD_TRANSLATE_SPA = 5, + NFIT_CMD_ARS_INJECT_SET = 7, + NFIT_CMD_ARS_INJECT_CLEAR = 8, + NFIT_CMD_ARS_INJECT_GET = 9, +}; + +struct nd_cmd_translate_spa { + __u64 spa; + __u32 status; + __u8 flags; + __u8 _reserved[3]; + __u64 translate_length; + __u32 num_nvdimms; + struct nd_nvdimm_device { + __u32 nfit_device_handle; + __u32 _reserved; + __u64 dpa; + } __packed devices[0]; + +} __packed; + +struct nd_cmd_ars_err_inj { + __u64 err_inj_spa_range_base; + __u64 err_inj_spa_range_length; + __u8 err_inj_options; + __u32 status; +} __packed; + +struct nd_cmd_ars_err_inj_clr { + __u64 err_inj_clr_spa_range_base; + __u64 err_inj_clr_spa_range_length; + __u32 status; +} __packed; + +struct nd_cmd_ars_err_inj_stat { + __u32 status; + __u32 inj_err_rec_count; + struct nd_error_stat_query_record { + __u64 err_inj_stat_spa_range_base; + __u64 err_inj_stat_spa_range_length; + } __packed record[0]; +} __packed; + union acpi_object; typedef void *acpi_handle; diff --git a/tools/testing/radix-tree/multiorder.c b/tools/testing/radix-tree/multiorder.c index 06c71178d07d..59245b3d587c 100644 --- a/tools/testing/radix-tree/multiorder.c +++ b/tools/testing/radix-tree/multiorder.c @@ -618,7 +618,7 @@ static void multiorder_account(void) __radix_tree_insert(&tree, 1 << 5, 5, (void *)0x12); __radix_tree_lookup(&tree, 1 << 5, &node, &slot); assert(node->count == node->exceptional * 2); - __radix_tree_replace(&tree, node, slot, NULL, NULL, NULL); + __radix_tree_replace(&tree, node, slot, NULL, NULL); assert(node->exceptional == 0); item_kill_tree(&tree); diff --git a/tools/testing/scatterlist/Makefile b/tools/testing/scatterlist/Makefile new file mode 100644 index 000000000000..933c3a6e4d77 --- /dev/null +++ b/tools/testing/scatterlist/Makefile @@ -0,0 +1,30 @@ +CFLAGS += -I. -I../../include -g -O2 -Wall -fsanitize=address +LDFLAGS += -fsanitize=address -fsanitize=undefined +TARGETS = main +OFILES = main.o scatterlist.o + +ifeq ($(BUILD), 32) + CFLAGS += -m32 + LDFLAGS += -m32 +endif + +targets: include $(TARGETS) + +main: $(OFILES) + +clean: + $(RM) $(TARGETS) $(OFILES) scatterlist.c linux/scatterlist.h linux/highmem.h linux/kmemleak.h asm/io.h + @rmdir asm + +scatterlist.c: ../../../lib/scatterlist.c + @sed -e 's/^static //' -e 's/__always_inline //' -e 's/inline //' < $< > $@ + +.PHONY: include + +include: ../../../include/linux/scatterlist.h + @mkdir -p linux + @mkdir -p asm + @touch asm/io.h + @touch linux/highmem.h + @touch linux/kmemleak.h + @cp $< linux/scatterlist.h diff --git a/tools/testing/scatterlist/linux/mm.h b/tools/testing/scatterlist/linux/mm.h new file mode 100644 index 000000000000..6f9ac14aa800 --- /dev/null +++ b/tools/testing/scatterlist/linux/mm.h @@ -0,0 +1,125 @@ +#ifndef _LINUX_MM_H +#define _LINUX_MM_H + +#include <assert.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> + +typedef unsigned long dma_addr_t; + +#define unlikely + +#define BUG_ON(x) assert(!(x)) + +#define WARN_ON(condition) ({ \ + int __ret_warn_on = !!(condition); \ + unlikely(__ret_warn_on); \ +}) + +#define WARN_ON_ONCE(condition) ({ \ + int __ret_warn_on = !!(condition); \ + if (unlikely(__ret_warn_on)) \ + assert(0); \ + unlikely(__ret_warn_on); \ +}) + +#define PAGE_SIZE (4096) +#define PAGE_SHIFT (12) +#define PAGE_MASK (~(PAGE_SIZE-1)) + +#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1) +#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) +#define ALIGN(x, a) __ALIGN_KERNEL((x), (a)) + +#define PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE) + +#define offset_in_page(p) ((unsigned long)(p) & ~PAGE_MASK) + +#define virt_to_page(x) ((void *)x) +#define page_address(x) ((void *)x) + +static inline unsigned long page_to_phys(struct page *page) +{ + assert(0); + + return 0; +} + +#define page_to_pfn(page) ((unsigned long)(page) / PAGE_SIZE) +#define pfn_to_page(pfn) (void *)((pfn) * PAGE_SIZE) +#define nth_page(page,n) pfn_to_page(page_to_pfn((page)) + (n)) + +#define __min(t1, t2, min1, min2, x, y) ({ \ + t1 min1 = (x); \ + t2 min2 = (y); \ + (void) (&min1 == &min2); \ + min1 < min2 ? min1 : min2; }) + +#define ___PASTE(a,b) a##b +#define __PASTE(a,b) ___PASTE(a,b) + +#define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__) + +#define min(x, y) \ + __min(typeof(x), typeof(y), \ + __UNIQUE_ID(min1_), __UNIQUE_ID(min2_), \ + x, y) + +#define min_t(type, x, y) \ + __min(type, type, \ + __UNIQUE_ID(min1_), __UNIQUE_ID(min2_), \ + x, y) + +#define preemptible() (1) + +static inline void *kmap(struct page *page) +{ + assert(0); + + return NULL; +} + +static inline void *kmap_atomic(struct page *page) +{ + assert(0); + + return NULL; +} + +static inline void kunmap(void *addr) +{ + assert(0); +} + +static inline void kunmap_atomic(void *addr) +{ + assert(0); +} + +static inline unsigned long __get_free_page(unsigned int flags) +{ + return (unsigned long)malloc(PAGE_SIZE); +} + +static inline void free_page(unsigned long page) +{ + free((void *)page); +} + +static inline void *kmalloc(unsigned int size, unsigned int flags) +{ + return malloc(size); +} + +#define kfree(x) free(x) + +#define kmemleak_alloc(a, b, c, d) +#define kmemleak_free(a) + +#define PageSlab(p) (0) +#define flush_kernel_dcache_page(p) + +#endif diff --git a/tools/testing/scatterlist/main.c b/tools/testing/scatterlist/main.c new file mode 100644 index 000000000000..0a1464181226 --- /dev/null +++ b/tools/testing/scatterlist/main.c @@ -0,0 +1,79 @@ +#include <stdio.h> +#include <assert.h> + +#include <linux/scatterlist.h> + +#define MAX_PAGES (64) + +static void set_pages(struct page **pages, const unsigned *array, unsigned num) +{ + unsigned int i; + + assert(num < MAX_PAGES); + for (i = 0; i < num; i++) + pages[i] = (struct page *)(unsigned long) + ((1 + array[i]) * PAGE_SIZE); +} + +#define pfn(...) (unsigned []){ __VA_ARGS__ } + +int main(void) +{ + const unsigned int sgmax = SCATTERLIST_MAX_SEGMENT; + struct test { + int alloc_ret; + unsigned num_pages; + unsigned *pfn; + unsigned size; + unsigned int max_seg; + unsigned int expected_segments; + } *test, tests[] = { + { -EINVAL, 1, pfn(0), PAGE_SIZE, PAGE_SIZE + 1, 1 }, + { -EINVAL, 1, pfn(0), PAGE_SIZE, 0, 1 }, + { -EINVAL, 1, pfn(0), PAGE_SIZE, sgmax + 1, 1 }, + { 0, 1, pfn(0), PAGE_SIZE, sgmax, 1 }, + { 0, 1, pfn(0), 1, sgmax, 1 }, + { 0, 2, pfn(0, 1), 2 * PAGE_SIZE, sgmax, 1 }, + { 0, 2, pfn(1, 0), 2 * PAGE_SIZE, sgmax, 2 }, + { 0, 3, pfn(0, 1, 2), 3 * PAGE_SIZE, sgmax, 1 }, + { 0, 3, pfn(0, 2, 1), 3 * PAGE_SIZE, sgmax, 3 }, + { 0, 3, pfn(0, 1, 3), 3 * PAGE_SIZE, sgmax, 2 }, + { 0, 3, pfn(1, 2, 4), 3 * PAGE_SIZE, sgmax, 2 }, + { 0, 3, pfn(1, 3, 4), 3 * PAGE_SIZE, sgmax, 2 }, + { 0, 4, pfn(0, 1, 3, 4), 4 * PAGE_SIZE, sgmax, 2 }, + { 0, 5, pfn(0, 1, 3, 4, 5), 5 * PAGE_SIZE, sgmax, 2 }, + { 0, 5, pfn(0, 1, 3, 4, 6), 5 * PAGE_SIZE, sgmax, 3 }, + { 0, 5, pfn(0, 1, 2, 3, 4), 5 * PAGE_SIZE, sgmax, 1 }, + { 0, 5, pfn(0, 1, 2, 3, 4), 5 * PAGE_SIZE, 2 * PAGE_SIZE, 3 }, + { 0, 6, pfn(0, 1, 2, 3, 4, 5), 6 * PAGE_SIZE, 2 * PAGE_SIZE, 3 }, + { 0, 6, pfn(0, 2, 3, 4, 5, 6), 6 * PAGE_SIZE, 2 * PAGE_SIZE, 4 }, + { 0, 6, pfn(0, 1, 3, 4, 5, 6), 6 * PAGE_SIZE, 2 * PAGE_SIZE, 3 }, + { 0, 0, NULL, 0, 0, 0 }, + }; + unsigned int i; + + for (i = 0, test = tests; test->expected_segments; test++, i++) { + struct page *pages[MAX_PAGES]; + struct sg_table st; + int ret; + + set_pages(pages, test->pfn, test->num_pages); + + ret = __sg_alloc_table_from_pages(&st, pages, test->num_pages, + 0, test->size, test->max_seg, + GFP_KERNEL); + assert(ret == test->alloc_ret); + + if (test->alloc_ret) + continue; + + assert(st.nents == test->expected_segments); + assert(st.orig_nents == test->expected_segments); + + sg_free_table(&st); + } + + assert(i == (sizeof(tests) / sizeof(tests[0])) - 1); + + return 0; +} diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 3c9c0bbe7dbb..eaf599dc2137 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -TARGETS = bpf +TARGETS = android +TARGETS += bpf TARGETS += breakpoints TARGETS += capabilities TARGETS += cpufreq diff --git a/tools/testing/selftests/android/Makefile b/tools/testing/selftests/android/Makefile new file mode 100644 index 000000000000..1a7492268993 --- /dev/null +++ b/tools/testing/selftests/android/Makefile @@ -0,0 +1,46 @@ +SUBDIRS := ion + +TEST_PROGS := run.sh + +.PHONY: all clean + +include ../lib.mk + +all: + @for DIR in $(SUBDIRS); do \ + BUILD_TARGET=$(OUTPUT)/$$DIR; \ + mkdir $$BUILD_TARGET -p; \ + make OUTPUT=$$BUILD_TARGET -C $$DIR $@;\ + #SUBDIR test prog name should be in the form: SUBDIR_test.sh + TEST=$$DIR"_test.sh"; \ + if [ -e $$DIR/$$TEST ]; then + rsync -a $$DIR/$$TEST $$BUILD_TARGET/; + fi + done + +override define RUN_TESTS + @cd $(OUTPUT); ./run.sh +endef + +override define INSTALL_RULE + mkdir -p $(INSTALL_PATH) + install -t $(INSTALL_PATH) $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES) + + @for SUBDIR in $(SUBDIRS); do \ + BUILD_TARGET=$(OUTPUT)/$$SUBDIR; \ + mkdir $$BUILD_TARGET -p; \ + $(MAKE) OUTPUT=$$BUILD_TARGET -C $$SUBDIR INSTALL_PATH=$(INSTALL_PATH)/$$SUBDIR install; \ + done; +endef + +override define EMIT_TESTS + echo "./run.sh" +endef + +override define CLEAN + @for DIR in $(SUBDIRS); do \ + BUILD_TARGET=$(OUTPUT)/$$DIR; \ + mkdir $$BUILD_TARGET -p; \ + make OUTPUT=$$BUILD_TARGET -C $$DIR $@;\ + done +endef diff --git a/tools/testing/selftests/android/ion/.gitignore b/tools/testing/selftests/android/ion/.gitignore new file mode 100644 index 000000000000..67e6f391b2a9 --- /dev/null +++ b/tools/testing/selftests/android/ion/.gitignore @@ -0,0 +1,2 @@ +ionapp_export +ionapp_import diff --git a/tools/testing/selftests/android/ion/Makefile b/tools/testing/selftests/android/ion/Makefile new file mode 100644 index 000000000000..96e0c448b39d --- /dev/null +++ b/tools/testing/selftests/android/ion/Makefile @@ -0,0 +1,16 @@ + +INCLUDEDIR := -I. -I../../../../../drivers/staging/android/uapi/ +CFLAGS := $(CFLAGS) $(INCLUDEDIR) -Wall -O2 -g + +TEST_GEN_FILES := ionapp_export ionapp_import + +all: $(TEST_GEN_FILES) + +$(TEST_GEN_FILES): ipcsocket.c ionutils.c + +TEST_PROGS := ion_test.sh + +include ../../lib.mk + +$(OUTPUT)/ionapp_export: ionapp_export.c ipcsocket.c ionutils.c +$(OUTPUT)/ionapp_import: ionapp_import.c ipcsocket.c ionutils.c diff --git a/tools/testing/selftests/android/ion/README b/tools/testing/selftests/android/ion/README new file mode 100644 index 000000000000..21783e9c451e --- /dev/null +++ b/tools/testing/selftests/android/ion/README @@ -0,0 +1,101 @@ +ION BUFFER SHARING UTILITY +========================== +File: ion_test.sh : Utility to test ION driver buffer sharing mechanism. +Author: Pintu Kumar <pintu.ping@gmail.com> + +Introduction: +------------- +This is a test utility to verify ION buffer sharing in user space +between 2 independent processes. +It uses unix domain socket (with SCM_RIGHTS) as IPC to transfer an FD to +another process to share the same buffer. +This utility demonstrates how ION buffer sharing can be implemented between +two user space processes, using various heap types. +The following heap types are supported by ION driver. +ION_HEAP_TYPE_SYSTEM (0) +ION_HEAP_TYPE_SYSTEM_CONTIG (1) +ION_HEAP_TYPE_CARVEOUT (2) +ION_HEAP_TYPE_CHUNK (3) +ION_HEAP_TYPE_DMA (4) + +By default only the SYSTEM and SYSTEM_CONTIG heaps are supported. +Each heap is associated with the respective heap id. +This utility is designed in the form of client/server program. +The server part (ionapp_export) is the exporter of the buffer. +It is responsible for creating an ION client, allocating the buffer based on +the heap id, writing some data to this buffer and then exporting the FD +(associated with this buffer) to another process using socket IPC. +This FD is called as buffer FD (which is different than the ION client FD). + +The client part (ionapp_import) is the importer of the buffer. +It retrives the FD from the socket data and installs into its address space. +This new FD internally points to the same kernel buffer. +So first it reads the data that is stored in this buffer and prints it. +Then it writes the different size of data (it could be different data) to the +same buffer. +Finally the buffer FD must be closed by both the exporter and importer. +Thus the same kernel buffer is shared among two user space processes using +ION driver and only one time allocation. + +Prerequisite: +------------- +This utility works only if /dev/ion interface is present. +The following configs needs to be enabled in kernel to include ion driver. +CONFIG_ANDROID=y +CONFIG_STAGING=y +CONFIG_ION=y +CONFIG_ION_SYSTEM_HEAP=y + +This utility requires to be run as root user. + + +Compile and test: +----------------- +This utility is made to be run as part of kselftest framework in kernel. +To compile and run using kselftest you can simply do the following from the +kernel top directory. +linux$ make TARGETS=android kselftest +Or you can also use: +linux$ make -C tools/testing/selftests TARGETS=android run_tests +Using the selftest it can directly execute the ion_test.sh script to test the +buffer sharing using ion system heap. +Currently the heap size is hard coded as just 10 bytes inside this script. +You need to be a root user to run under selftest. + +You can also compile and test manually using the following steps: +ion$ make +These will generate 2 executable: ionapp_export, ionapp_import +Now you can run the export and import manually by specifying the heap type +and the heap size. +You can also directly execute the shell script to run the test automatically. +Simply use the following command to run the test. +ion$ sudo ./ion_test.sh + +Test Results: +------------- +The utility is verified on Ubuntu-32 bit system with Linux Kernel 4.14. +Here is the snapshot of the test result using kselftest. + +linux# make TARGETS=android kselftest +heap_type: 0, heap_size: 10 +-------------------------------------- +heap type: 0 + heap id: 1 +heap name: ion_system_heap +-------------------------------------- +Fill buffer content: +0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd +Sharing fd: 6, Client fd: 5 +<ion_close_buffer_fd>: buffer release successfully.... +Received buffer fd: 4 +Read buffer content: +0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0x0 0x0 0x0 0x0 0x0 0x0 +0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 +Fill buffer content: +0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd +0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd +0xfd 0xfd +<ion_close_buffer_fd>: buffer release successfully.... +ion_test.sh: heap_type: 0 - [PASS] + +ion_test.sh: done diff --git a/tools/testing/selftests/android/ion/config b/tools/testing/selftests/android/ion/config new file mode 100644 index 000000000000..19db6ca9aa2b --- /dev/null +++ b/tools/testing/selftests/android/ion/config @@ -0,0 +1,4 @@ +CONFIG_ANDROID=y +CONFIG_STAGING=y +CONFIG_ION=y +CONFIG_ION_SYSTEM_HEAP=y diff --git a/tools/testing/selftests/android/ion/ion.h b/tools/testing/selftests/android/ion/ion.h new file mode 100644 index 000000000000..f7021ac51335 --- /dev/null +++ b/tools/testing/selftests/android/ion/ion.h @@ -0,0 +1,143 @@ +/* + * ion.h + * + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* This file is copied from drivers/staging/android/uapi/ion.h + * This local copy is required for the selftest to pass, when build + * outside the kernel source tree. + * Please keep this file in sync with its original file until the + * ion driver is moved outside the staging tree. + */ + +#ifndef _UAPI_LINUX_ION_H +#define _UAPI_LINUX_ION_H + +#include <linux/ioctl.h> +#include <linux/types.h> + +/** + * enum ion_heap_types - list of all possible types of heaps + * @ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc + * @ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kmalloc + * @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved + * carveout heap, allocations are physically + * contiguous + * @ION_HEAP_TYPE_DMA: memory allocated via DMA API + * @ION_NUM_HEAPS: helper for iterating over heaps, a bit mask + * is used to identify the heaps, so only 32 + * total heap types are supported + */ +enum ion_heap_type { + ION_HEAP_TYPE_SYSTEM, + ION_HEAP_TYPE_SYSTEM_CONTIG, + ION_HEAP_TYPE_CARVEOUT, + ION_HEAP_TYPE_CHUNK, + ION_HEAP_TYPE_DMA, + ION_HEAP_TYPE_CUSTOM, /* + * must be last so device specific heaps always + * are at the end of this enum + */ +}; + +#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8) + +/** + * allocation flags - the lower 16 bits are used by core ion, the upper 16 + * bits are reserved for use by the heaps themselves. + */ + +/* + * mappings of this buffer should be cached, ion will do cache maintenance + * when the buffer is mapped for dma + */ +#define ION_FLAG_CACHED 1 + +/** + * DOC: Ion Userspace API + * + * create a client by opening /dev/ion + * most operations handled via following ioctls + * + */ + +/** + * struct ion_allocation_data - metadata passed from userspace for allocations + * @len: size of the allocation + * @heap_id_mask: mask of heap ids to allocate from + * @flags: flags passed to heap + * @handle: pointer that will be populated with a cookie to use to + * refer to this allocation + * + * Provided by userspace as an argument to the ioctl + */ +struct ion_allocation_data { + __u64 len; + __u32 heap_id_mask; + __u32 flags; + __u32 fd; + __u32 unused; +}; + +#define MAX_HEAP_NAME 32 + +/** + * struct ion_heap_data - data about a heap + * @name - first 32 characters of the heap name + * @type - heap type + * @heap_id - heap id for the heap + */ +struct ion_heap_data { + char name[MAX_HEAP_NAME]; + __u32 type; + __u32 heap_id; + __u32 reserved0; + __u32 reserved1; + __u32 reserved2; +}; + +/** + * struct ion_heap_query - collection of data about all heaps + * @cnt - total number of heaps to be copied + * @heaps - buffer to copy heap data + */ +struct ion_heap_query { + __u32 cnt; /* Total number of heaps to be copied */ + __u32 reserved0; /* align to 64bits */ + __u64 heaps; /* buffer to be populated */ + __u32 reserved1; + __u32 reserved2; +}; + +#define ION_IOC_MAGIC 'I' + +/** + * DOC: ION_IOC_ALLOC - allocate memory + * + * Takes an ion_allocation_data struct and returns it with the handle field + * populated with the opaque handle for the allocation. + */ +#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \ + struct ion_allocation_data) + +/** + * DOC: ION_IOC_HEAP_QUERY - information about available heaps + * + * Takes an ion_heap_query structure and populates information about + * available Ion heaps. + */ +#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, \ + struct ion_heap_query) + +#endif /* _UAPI_LINUX_ION_H */ diff --git a/tools/testing/selftests/android/ion/ion_test.sh b/tools/testing/selftests/android/ion/ion_test.sh new file mode 100755 index 000000000000..a1aff506f5e6 --- /dev/null +++ b/tools/testing/selftests/android/ion/ion_test.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +heapsize=4096 +TCID="ion_test.sh" +errcode=0 + +run_test() +{ + heaptype=$1 + ./ionapp_export -i $heaptype -s $heapsize & + sleep 1 + ./ionapp_import + if [ $? -ne 0 ]; then + echo "$TCID: heap_type: $heaptype - [FAIL]" + errcode=1 + else + echo "$TCID: heap_type: $heaptype - [PASS]" + fi + sleep 1 + echo "" +} + +check_root() +{ + uid=$(id -u) + if [ $uid -ne 0 ]; then + echo $TCID: must be run as root >&2 + exit 0 + fi +} + +check_device() +{ + DEVICE=/dev/ion + if [ ! -e $DEVICE ]; then + echo $TCID: No $DEVICE device found >&2 + echo $TCID: May be CONFIG_ION is not set >&2 + exit 0 + fi +} + +main_function() +{ + check_device + check_root + + # ION_SYSTEM_HEAP TEST + run_test 0 + # ION_SYSTEM_CONTIG_HEAP TEST + run_test 1 +} + +main_function +echo "$TCID: done" +exit $errcode diff --git a/tools/testing/selftests/android/ion/ionapp_export.c b/tools/testing/selftests/android/ion/ionapp_export.c new file mode 100644 index 000000000000..a944e72621a9 --- /dev/null +++ b/tools/testing/selftests/android/ion/ionapp_export.c @@ -0,0 +1,135 @@ +/* + * ionapp_export.c + * + * It is a user space utility to create and export android + * ion memory buffer fd to another process using unix domain socket as IPC. + * This acts like a server for ionapp_import(client). + * So, this server has to be started first before the client. + * + * Copyright (C) 2017 Pintu Kumar <pintu.ping@gmail.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/time.h> +#include "ionutils.h" +#include "ipcsocket.h" + + +void print_usage(int argc, char *argv[]) +{ + printf("Usage: %s [-h <help>] [-i <heap id>] [-s <size in bytes>]\n", + argv[0]); +} + +int main(int argc, char *argv[]) +{ + int opt, ret, status, heapid; + int sockfd, client_fd, shared_fd; + unsigned char *map_buf; + unsigned long map_len, heap_type, heap_size, flags; + struct ion_buffer_info info; + struct socket_info skinfo; + + if (argc < 2) { + print_usage(argc, argv); + return -1; + } + + heap_size = 0; + flags = 0; + + while ((opt = getopt(argc, argv, "hi:s:")) != -1) { + switch (opt) { + case 'h': + print_usage(argc, argv); + exit(0); + break; + case 'i': + heapid = atoi(optarg); + switch (heapid) { + case 0: + heap_type = ION_HEAP_TYPE_SYSTEM; + break; + case 1: + heap_type = ION_HEAP_TYPE_SYSTEM_CONTIG; + break; + default: + printf("ERROR: heap type not supported\n"); + exit(1); + } + break; + case 's': + heap_size = atoi(optarg); + break; + default: + print_usage(argc, argv); + exit(1); + break; + } + } + + if (heap_size <= 0) { + printf("heap_size cannot be 0\n"); + print_usage(argc, argv); + exit(1); + } + + printf("heap_type: %ld, heap_size: %ld\n", heap_type, heap_size); + info.heap_type = heap_type; + info.heap_size = heap_size; + info.flag_type = flags; + + /* This is server: open the socket connection first */ + /* Here; 1 indicates server or exporter */ + status = opensocket(&sockfd, SOCKET_NAME, 1); + if (status < 0) { + fprintf(stderr, "<%s>: Failed opensocket.\n", __func__); + goto err_socket; + } + skinfo.sockfd = sockfd; + + ret = ion_export_buffer_fd(&info); + if (ret < 0) { + fprintf(stderr, "FAILED: ion_get_buffer_fd\n"); + goto err_export; + } + client_fd = info.ionfd; + shared_fd = info.buffd; + map_buf = info.buffer; + map_len = info.buflen; + write_buffer(map_buf, map_len); + + /* share ion buf fd with other user process */ + printf("Sharing fd: %d, Client fd: %d\n", shared_fd, client_fd); + skinfo.datafd = shared_fd; + skinfo.buflen = map_len; + + ret = socket_send_fd(&skinfo); + if (ret < 0) { + fprintf(stderr, "FAILED: socket_send_fd\n"); + goto err_send; + } + +err_send: +err_export: + ion_close_buffer_fd(&info); + +err_socket: + closesocket(sockfd, SOCKET_NAME); + + return 0; +} diff --git a/tools/testing/selftests/android/ion/ionapp_import.c b/tools/testing/selftests/android/ion/ionapp_import.c new file mode 100644 index 000000000000..ae2d704cfa46 --- /dev/null +++ b/tools/testing/selftests/android/ion/ionapp_import.c @@ -0,0 +1,88 @@ +/* + * ionapp_import.c + * + * It is a user space utility to receive android ion memory buffer fd + * over unix domain socket IPC that can be exported by ionapp_export. + * This acts like a client for ionapp_export. + * + * Copyright (C) 2017 Pintu Kumar <pintu.ping@gmail.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include "ionutils.h" +#include "ipcsocket.h" + + +int main(void) +{ + int ret, status; + int sockfd, shared_fd; + unsigned char *map_buf; + unsigned long map_len; + struct ion_buffer_info info; + struct socket_info skinfo; + + /* This is the client part. Here 0 means client or importer */ + status = opensocket(&sockfd, SOCKET_NAME, 0); + if (status < 0) { + fprintf(stderr, "No exporter exists...\n"); + ret = status; + goto err_socket; + } + + skinfo.sockfd = sockfd; + + ret = socket_receive_fd(&skinfo); + if (ret < 0) { + fprintf(stderr, "Failed: socket_receive_fd\n"); + goto err_recv; + } + + shared_fd = skinfo.datafd; + printf("Received buffer fd: %d\n", shared_fd); + if (shared_fd <= 0) { + fprintf(stderr, "ERROR: improper buf fd\n"); + ret = -1; + goto err_fd; + } + + memset(&info, 0, sizeof(info)); + info.buffd = shared_fd; + info.buflen = ION_BUFFER_LEN; + + ret = ion_import_buffer_fd(&info); + if (ret < 0) { + fprintf(stderr, "Failed: ion_use_buffer_fd\n"); + goto err_import; + } + + map_buf = info.buffer; + map_len = info.buflen; + read_buffer(map_buf, map_len); + + /* Write probably new data to the same buffer again */ + map_len = ION_BUFFER_LEN; + write_buffer(map_buf, map_len); + +err_import: + ion_close_buffer_fd(&info); +err_fd: +err_recv: +err_socket: + closesocket(sockfd, SOCKET_NAME); + + return ret; +} diff --git a/tools/testing/selftests/android/ion/ionutils.c b/tools/testing/selftests/android/ion/ionutils.c new file mode 100644 index 000000000000..ce69c14f51fa --- /dev/null +++ b/tools/testing/selftests/android/ion/ionutils.c @@ -0,0 +1,259 @@ +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +//#include <stdint.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include "ionutils.h" +#include "ipcsocket.h" + + +void write_buffer(void *buffer, unsigned long len) +{ + int i; + unsigned char *ptr = (unsigned char *)buffer; + + if (!ptr) { + fprintf(stderr, "<%s>: Invalid buffer...\n", __func__); + return; + } + + printf("Fill buffer content:\n"); + memset(ptr, 0xfd, len); + for (i = 0; i < len; i++) + printf("0x%x ", ptr[i]); + printf("\n"); +} + +void read_buffer(void *buffer, unsigned long len) +{ + int i; + unsigned char *ptr = (unsigned char *)buffer; + + if (!ptr) { + fprintf(stderr, "<%s>: Invalid buffer...\n", __func__); + return; + } + + printf("Read buffer content:\n"); + for (i = 0; i < len; i++) + printf("0x%x ", ptr[i]); + printf("\n"); +} + +int ion_export_buffer_fd(struct ion_buffer_info *ion_info) +{ + int i, ret, ionfd, buffer_fd; + unsigned int heap_id; + unsigned long maplen; + unsigned char *map_buffer; + struct ion_allocation_data alloc_data; + struct ion_heap_query query; + struct ion_heap_data heap_data[MAX_HEAP_COUNT]; + + if (!ion_info) { + fprintf(stderr, "<%s>: Invalid ion info\n", __func__); + return -1; + } + + /* Create an ION client */ + ionfd = open(ION_DEVICE, O_RDWR); + if (ionfd < 0) { + fprintf(stderr, "<%s>: Failed to open ion client: %s\n", + __func__, strerror(errno)); + return -1; + } + + memset(&query, 0, sizeof(query)); + query.cnt = MAX_HEAP_COUNT; + query.heaps = (unsigned long int)&heap_data[0]; + /* Query ION heap_id_mask from ION heap */ + ret = ioctl(ionfd, ION_IOC_HEAP_QUERY, &query); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed: ION_IOC_HEAP_QUERY: %s\n", + __func__, strerror(errno)); + goto err_query; + } + + heap_id = MAX_HEAP_COUNT + 1; + for (i = 0; i < query.cnt; i++) { + if (heap_data[i].type == ion_info->heap_type) { + printf("--------------------------------------\n"); + printf("heap type: %d\n", heap_data[i].type); + printf(" heap id: %d\n", heap_data[i].heap_id); + printf("heap name: %s\n", heap_data[i].name); + printf("--------------------------------------\n"); + heap_id = heap_data[i].heap_id; + break; + } + } + + if (heap_id > MAX_HEAP_COUNT) { + fprintf(stderr, "<%s>: ERROR: heap type does not exists\n", + __func__); + goto err_heap; + } + + alloc_data.len = ion_info->heap_size; + alloc_data.heap_id_mask = 1 << heap_id; + alloc_data.flags = ion_info->flag_type; + + /* Allocate memory for this ION client as per heap_type */ + ret = ioctl(ionfd, ION_IOC_ALLOC, &alloc_data); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed: ION_IOC_ALLOC: %s\n", + __func__, strerror(errno)); + goto err_alloc; + } + + /* This will return a valid buffer fd */ + buffer_fd = alloc_data.fd; + maplen = alloc_data.len; + + if (buffer_fd < 0 || maplen <= 0) { + fprintf(stderr, "<%s>: Invalid map data, fd: %d, len: %ld\n", + __func__, buffer_fd, maplen); + goto err_fd_data; + } + + /* Create memory mapped buffer for the buffer fd */ + map_buffer = (unsigned char *)mmap(NULL, maplen, PROT_READ|PROT_WRITE, + MAP_SHARED, buffer_fd, 0); + if (map_buffer == MAP_FAILED) { + fprintf(stderr, "<%s>: Failed: mmap: %s\n", + __func__, strerror(errno)); + goto err_mmap; + } + + ion_info->ionfd = ionfd; + ion_info->buffd = buffer_fd; + ion_info->buffer = map_buffer; + ion_info->buflen = maplen; + + return 0; + + munmap(map_buffer, maplen); + +err_fd_data: +err_mmap: + /* in case of error: close the buffer fd */ + if (buffer_fd) + close(buffer_fd); + +err_query: +err_heap: +err_alloc: + /* In case of error: close the ion client fd */ + if (ionfd) + close(ionfd); + + return -1; +} + +int ion_import_buffer_fd(struct ion_buffer_info *ion_info) +{ + int buffd; + unsigned char *map_buf; + unsigned long map_len; + + if (!ion_info) { + fprintf(stderr, "<%s>: Invalid ion info\n", __func__); + return -1; + } + + map_len = ion_info->buflen; + buffd = ion_info->buffd; + + if (buffd < 0 || map_len <= 0) { + fprintf(stderr, "<%s>: Invalid map data, fd: %d, len: %ld\n", + __func__, buffd, map_len); + goto err_buffd; + } + + map_buf = (unsigned char *)mmap(NULL, map_len, PROT_READ|PROT_WRITE, + MAP_SHARED, buffd, 0); + if (map_buf == MAP_FAILED) { + printf("<%s>: Failed - mmap: %s\n", + __func__, strerror(errno)); + goto err_mmap; + } + + ion_info->buffer = map_buf; + ion_info->buflen = map_len; + + return 0; + +err_mmap: + if (buffd) + close(buffd); + +err_buffd: + return -1; +} + +void ion_close_buffer_fd(struct ion_buffer_info *ion_info) +{ + if (ion_info) { + /* unmap the buffer properly in the end */ + munmap(ion_info->buffer, ion_info->buflen); + /* close the buffer fd */ + if (ion_info->buffd > 0) + close(ion_info->buffd); + /* Finally, close the client fd */ + if (ion_info->ionfd > 0) + close(ion_info->ionfd); + printf("<%s>: buffer release successfully....\n", __func__); + } +} + +int socket_send_fd(struct socket_info *info) +{ + int status; + int fd, sockfd; + struct socketdata skdata; + + if (!info) { + fprintf(stderr, "<%s>: Invalid socket info\n", __func__); + return -1; + } + + sockfd = info->sockfd; + fd = info->datafd; + memset(&skdata, 0, sizeof(skdata)); + skdata.data = fd; + skdata.len = sizeof(skdata.data); + status = sendtosocket(sockfd, &skdata); + if (status < 0) { + fprintf(stderr, "<%s>: Failed: sendtosocket\n", __func__); + return -1; + } + + return 0; +} + +int socket_receive_fd(struct socket_info *info) +{ + int status; + int fd, sockfd; + struct socketdata skdata; + + if (!info) { + fprintf(stderr, "<%s>: Invalid socket info\n", __func__); + return -1; + } + + sockfd = info->sockfd; + memset(&skdata, 0, sizeof(skdata)); + status = receivefromsocket(sockfd, &skdata); + if (status < 0) { + fprintf(stderr, "<%s>: Failed: receivefromsocket\n", __func__); + return -1; + } + + fd = (int)skdata.data; + info->datafd = fd; + + return status; +} diff --git a/tools/testing/selftests/android/ion/ionutils.h b/tools/testing/selftests/android/ion/ionutils.h new file mode 100644 index 000000000000..9941eb858576 --- /dev/null +++ b/tools/testing/selftests/android/ion/ionutils.h @@ -0,0 +1,55 @@ +#ifndef __ION_UTILS_H +#define __ION_UTILS_H + +#include "ion.h" + +#define SOCKET_NAME "ion_socket" +#define ION_DEVICE "/dev/ion" + +#define ION_BUFFER_LEN 4096 +#define MAX_HEAP_COUNT ION_HEAP_TYPE_CUSTOM + +struct socket_info { + int sockfd; + int datafd; + unsigned long buflen; +}; + +struct ion_buffer_info { + int ionfd; + int buffd; + unsigned int heap_type; + unsigned int flag_type; + unsigned long heap_size; + unsigned long buflen; + unsigned char *buffer; +}; + + +/* This is used to fill the data into the mapped buffer */ +void write_buffer(void *buffer, unsigned long len); + +/* This is used to read the data from the exported buffer */ +void read_buffer(void *buffer, unsigned long len); + +/* This is used to create an ION buffer FD for the kernel buffer + * So you can export this same buffer to others in the form of FD + */ +int ion_export_buffer_fd(struct ion_buffer_info *ion_info); + +/* This is used to import or map an exported FD. + * So we point to same buffer without making a copy. Hence zero-copy. + */ +int ion_import_buffer_fd(struct ion_buffer_info *ion_info); + +/* This is used to close all references for the ION client */ +void ion_close_buffer_fd(struct ion_buffer_info *ion_info); + +/* This is used to send FD to another process using socket IPC */ +int socket_send_fd(struct socket_info *skinfo); + +/* This is used to receive FD from another process using socket IPC */ +int socket_receive_fd(struct socket_info *skinfo); + + +#endif diff --git a/tools/testing/selftests/android/ion/ipcsocket.c b/tools/testing/selftests/android/ion/ipcsocket.c new file mode 100644 index 000000000000..7dc521002095 --- /dev/null +++ b/tools/testing/selftests/android/ion/ipcsocket.c @@ -0,0 +1,227 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/un.h> +#include <errno.h> + +#include "ipcsocket.h" + + +int opensocket(int *sockfd, const char *name, int connecttype) +{ + int ret, temp = 1; + + if (!name || strlen(name) > MAX_SOCK_NAME_LEN) { + fprintf(stderr, "<%s>: Invalid socket name.\n", __func__); + return -1; + } + + ret = socket(PF_LOCAL, SOCK_STREAM, 0); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed socket: <%s>\n", + __func__, strerror(errno)); + return ret; + } + + *sockfd = ret; + if (setsockopt(*sockfd, SOL_SOCKET, SO_REUSEADDR, + (char *)&temp, sizeof(int)) < 0) { + fprintf(stderr, "<%s>: Failed setsockopt: <%s>\n", + __func__, strerror(errno)); + goto err; + } + + sprintf(sock_name, "/tmp/%s", name); + + if (connecttype == 1) { + /* This is for Server connection */ + struct sockaddr_un skaddr; + int clientfd; + socklen_t sklen; + + unlink(sock_name); + memset(&skaddr, 0, sizeof(skaddr)); + skaddr.sun_family = AF_LOCAL; + strcpy(skaddr.sun_path, sock_name); + + ret = bind(*sockfd, (struct sockaddr *)&skaddr, + SUN_LEN(&skaddr)); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed bind: <%s>\n", + __func__, strerror(errno)); + goto err; + } + + ret = listen(*sockfd, 5); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed listen: <%s>\n", + __func__, strerror(errno)); + goto err; + } + + memset(&skaddr, 0, sizeof(skaddr)); + sklen = sizeof(skaddr); + + ret = accept(*sockfd, (struct sockaddr *)&skaddr, + (socklen_t *)&sklen); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed accept: <%s>\n", + __func__, strerror(errno)); + goto err; + } + + clientfd = ret; + *sockfd = clientfd; + } else { + /* This is for client connection */ + struct sockaddr_un skaddr; + + memset(&skaddr, 0, sizeof(skaddr)); + skaddr.sun_family = AF_LOCAL; + strcpy(skaddr.sun_path, sock_name); + + ret = connect(*sockfd, (struct sockaddr *)&skaddr, + SUN_LEN(&skaddr)); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed connect: <%s>\n", + __func__, strerror(errno)); + goto err; + } + } + + return 0; + +err: + if (*sockfd) + close(*sockfd); + + return ret; +} + +int sendtosocket(int sockfd, struct socketdata *skdata) +{ + int ret, buffd; + unsigned int len; + char cmsg_b[CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cmsg; + struct msghdr msgh; + struct iovec iov; + struct timeval timeout; + fd_set selFDs; + + if (!skdata) { + fprintf(stderr, "<%s>: socketdata is NULL\n", __func__); + return -1; + } + + FD_ZERO(&selFDs); + FD_SET(0, &selFDs); + FD_SET(sockfd, &selFDs); + timeout.tv_sec = 20; + timeout.tv_usec = 0; + + ret = select(sockfd+1, NULL, &selFDs, NULL, &timeout); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed select: <%s>\n", + __func__, strerror(errno)); + return -1; + } + + if (FD_ISSET(sockfd, &selFDs)) { + buffd = skdata->data; + len = skdata->len; + memset(&msgh, 0, sizeof(msgh)); + msgh.msg_control = &cmsg_b; + msgh.msg_controllen = CMSG_LEN(len); + iov.iov_base = "OK"; + iov.iov_len = 2; + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + cmsg = CMSG_FIRSTHDR(&msgh); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(len); + memcpy(CMSG_DATA(cmsg), &buffd, len); + + ret = sendmsg(sockfd, &msgh, MSG_DONTWAIT); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed sendmsg: <%s>\n", + __func__, strerror(errno)); + return -1; + } + } + + return 0; +} + +int receivefromsocket(int sockfd, struct socketdata *skdata) +{ + int ret, buffd; + unsigned int len = 0; + char cmsg_b[CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cmsg; + struct msghdr msgh; + struct iovec iov; + fd_set recvFDs; + char data[32]; + + if (!skdata) { + fprintf(stderr, "<%s>: socketdata is NULL\n", __func__); + return -1; + } + + FD_ZERO(&recvFDs); + FD_SET(0, &recvFDs); + FD_SET(sockfd, &recvFDs); + + ret = select(sockfd+1, &recvFDs, NULL, NULL, NULL); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed select: <%s>\n", + __func__, strerror(errno)); + return -1; + } + + if (FD_ISSET(sockfd, &recvFDs)) { + len = sizeof(buffd); + memset(&msgh, 0, sizeof(msgh)); + msgh.msg_control = &cmsg_b; + msgh.msg_controllen = CMSG_LEN(len); + iov.iov_base = data; + iov.iov_len = sizeof(data)-1; + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + cmsg = CMSG_FIRSTHDR(&msgh); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(len); + + ret = recvmsg(sockfd, &msgh, MSG_DONTWAIT); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed recvmsg: <%s>\n", + __func__, strerror(errno)); + return -1; + } + + memcpy(&buffd, CMSG_DATA(cmsg), len); + skdata->data = buffd; + skdata->len = len; + } + return 0; +} + +int closesocket(int sockfd, char *name) +{ + char sockname[MAX_SOCK_NAME_LEN]; + + if (sockfd) + close(sockfd); + sprintf(sockname, "/tmp/%s", name); + unlink(sockname); + shutdown(sockfd, 2); + + return 0; +} diff --git a/tools/testing/selftests/android/ion/ipcsocket.h b/tools/testing/selftests/android/ion/ipcsocket.h new file mode 100644 index 000000000000..b3e84498a8a1 --- /dev/null +++ b/tools/testing/selftests/android/ion/ipcsocket.h @@ -0,0 +1,35 @@ + +#ifndef _IPCSOCKET_H +#define _IPCSOCKET_H + + +#define MAX_SOCK_NAME_LEN 64 + +char sock_name[MAX_SOCK_NAME_LEN]; + +/* This structure is responsible for holding the IPC data + * data: hold the buffer fd + * len: just the length of 32-bit integer fd + */ +struct socketdata { + int data; + unsigned int len; +}; + +/* This API is used to open the IPC socket connection + * name: implies a unique socket name in the system + * connecttype: implies server(0) or client(1) + */ +int opensocket(int *sockfd, const char *name, int connecttype); + +/* This is the API to send socket data over IPC socket */ +int sendtosocket(int sockfd, struct socketdata *data); + +/* This is the API to receive socket data over IPC socket */ +int receivefromsocket(int sockfd, struct socketdata *data); + +/* This is the API to close the socket connection */ +int closesocket(int sockfd, char *name); + + +#endif diff --git a/tools/testing/selftests/android/run.sh b/tools/testing/selftests/android/run.sh new file mode 100755 index 000000000000..dd8edf291454 --- /dev/null +++ b/tools/testing/selftests/android/run.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +(cd ion; ./ion_test.sh) diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index bb3c4ad8c59f..bf092b83e453 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -3579,7 +3579,7 @@ static struct bpf_test tests[] = { .prog_type = BPF_PROG_TYPE_SCHED_CLS, }, { - "helper access to packet: test19, cls helper fail range zero", + "helper access to packet: test19, cls helper range zero", .insns = { BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, offsetof(struct __sk_buff, data)), @@ -3599,8 +3599,7 @@ static struct bpf_test tests[] = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .result = REJECT, - .errstr = "invalid access to packet", + .result = ACCEPT, .prog_type = BPF_PROG_TYPE_SCHED_CLS, }, { @@ -4379,10 +4378,10 @@ static struct bpf_test tests[] = { BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), - BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), - BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_1, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_0), BPF_MOV64_IMM(BPF_REG_3, 0), - BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EMIT_CALL(BPF_FUNC_probe_write_user), BPF_EXIT_INSN(), }, .fixup_map2 = { 3 }, @@ -4486,9 +4485,10 @@ static struct bpf_test tests[] = { BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, offsetof(struct test_val, foo)), - BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_1), + BPF_MOV64_IMM(BPF_REG_1, 0), BPF_MOV64_IMM(BPF_REG_3, 0), - BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EMIT_CALL(BPF_FUNC_probe_write_user), BPF_EXIT_INSN(), }, .fixup_map2 = { 3 }, @@ -4622,13 +4622,14 @@ static struct bpf_test tests[] = { BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), BPF_MOV64_IMM(BPF_REG_3, 0), BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), - BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_1), + BPF_MOV64_IMM(BPF_REG_1, 0), BPF_MOV64_IMM(BPF_REG_3, 0), - BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EMIT_CALL(BPF_FUNC_probe_write_user), BPF_EXIT_INSN(), }, .fixup_map2 = { 3 }, - .errstr = "R1 min value is outside of the array range", + .errstr = "R2 min value is outside of the array range", .result = REJECT, .prog_type = BPF_PROG_TYPE_TRACEPOINT, }, @@ -4765,13 +4766,14 @@ static struct bpf_test tests[] = { BPF_JMP_IMM(BPF_JGT, BPF_REG_3, offsetof(struct test_val, foo), 4), BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), - BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_1), + BPF_MOV64_IMM(BPF_REG_1, 0), BPF_MOV64_IMM(BPF_REG_3, 0), - BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EMIT_CALL(BPF_FUNC_probe_write_user), BPF_EXIT_INSN(), }, .fixup_map2 = { 3 }, - .errstr = "R1 min value is outside of the array range", + .errstr = "R2 min value is outside of the array range", .result = REJECT, .prog_type = BPF_PROG_TYPE_TRACEPOINT, }, @@ -5350,7 +5352,7 @@ static struct bpf_test tests[] = { BPF_EMIT_CALL(BPF_FUNC_probe_read), BPF_EXIT_INSN(), }, - .errstr = "invalid stack type R1 off=-64 access_size=0", + .errstr = "invalid indirect read from stack off -64+0 size 64", .result = REJECT, .prog_type = BPF_PROG_TYPE_TRACEPOINT, }, @@ -5505,7 +5507,7 @@ static struct bpf_test tests[] = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_EXIT_INSN(), }, - .errstr = "invalid stack type R1 off=-64 access_size=0", + .errstr = "invalid indirect read from stack off -64+0 size 64", .result = REJECT, .prog_type = BPF_PROG_TYPE_TRACEPOINT, }, @@ -5668,7 +5670,7 @@ static struct bpf_test tests[] = { .prog_type = BPF_PROG_TYPE_SCHED_CLS, }, { - "helper access to variable memory: size = 0 not allowed on != NULL", + "helper access to variable memory: size = 0 allowed on != NULL stack pointer", .insns = { BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), @@ -5681,8 +5683,99 @@ static struct bpf_test tests[] = { BPF_EMIT_CALL(BPF_FUNC_csum_diff), BPF_EXIT_INSN(), }, - .errstr = "invalid stack type R1 off=-8 access_size=0", - .result = REJECT, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to variable memory: size = 0 allowed on != NULL map pointer", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_MOV64_IMM(BPF_REG_5, 0), + BPF_EMIT_CALL(BPF_FUNC_csum_diff), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 3 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to variable memory: size possible = 0 allowed on != NULL stack pointer", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_0, 0), + BPF_JMP_IMM(BPF_JGT, BPF_REG_2, 8, 7), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_MOV64_IMM(BPF_REG_5, 0), + BPF_EMIT_CALL(BPF_FUNC_csum_diff), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 3 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to variable memory: size possible = 0 allowed on != NULL map pointer", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_0, 0), + BPF_JMP_IMM(BPF_JGT, BPF_REG_2, 8, 4), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_MOV64_IMM(BPF_REG_5, 0), + BPF_EMIT_CALL(BPF_FUNC_csum_diff), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 3 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "helper access to variable memory: size possible = 0 allowed on != NULL packet pointer", + .insns = { + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct __sk_buff, data)), + BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, + offsetof(struct __sk_buff, data_end)), + BPF_MOV64_REG(BPF_REG_0, BPF_REG_6), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 7), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_6, 0), + BPF_JMP_IMM(BPF_JGT, BPF_REG_2, 8, 4), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_MOV64_IMM(BPF_REG_4, 0), + BPF_MOV64_IMM(BPF_REG_5, 0), + BPF_EMIT_CALL(BPF_FUNC_csum_diff), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, .prog_type = BPF_PROG_TYPE_SCHED_CLS, }, { diff --git a/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c b/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c index 960d02100c26..2d95e5adde72 100644 --- a/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c +++ b/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c @@ -19,6 +19,7 @@ #define _GNU_SOURCE +#include <asm/ptrace.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/ptrace.h> diff --git a/tools/testing/selftests/cpu-hotplug/config b/tools/testing/selftests/cpu-hotplug/config index e6ab090cfbf3..d4aca2ad5069 100644 --- a/tools/testing/selftests/cpu-hotplug/config +++ b/tools/testing/selftests/cpu-hotplug/config @@ -1,2 +1 @@ CONFIG_NOTIFIER_ERROR_INJECTION=y -CONFIG_CPU_NOTIFIER_ERROR_INJECT=m diff --git a/tools/testing/selftests/exec/execveat.c b/tools/testing/selftests/exec/execveat.c index 8d5d1d2ee7c1..67cd4597db2b 100644 --- a/tools/testing/selftests/exec/execveat.c +++ b/tools/testing/selftests/exec/execveat.c @@ -147,7 +147,7 @@ static void exe_cp(const char *src, const char *dest) } #define XX_DIR_LEN 200 -static int check_execveat_pathmax(int dot_dfd, const char *src, int is_script) +static int check_execveat_pathmax(int root_dfd, const char *src, int is_script) { int fail = 0; int ii, count, len; @@ -156,20 +156,30 @@ static int check_execveat_pathmax(int dot_dfd, const char *src, int is_script) if (*longpath == '\0') { /* Create a filename close to PATH_MAX in length */ + char *cwd = getcwd(NULL, 0); + + if (!cwd) { + printf("Failed to getcwd(), errno=%d (%s)\n", + errno, strerror(errno)); + return 2; + } + strcpy(longpath, cwd); + strcat(longpath, "/"); memset(longname, 'x', XX_DIR_LEN - 1); longname[XX_DIR_LEN - 1] = '/'; longname[XX_DIR_LEN] = '\0'; - count = (PATH_MAX - 3) / XX_DIR_LEN; + count = (PATH_MAX - 3 - strlen(cwd)) / XX_DIR_LEN; for (ii = 0; ii < count; ii++) { strcat(longpath, longname); mkdir(longpath, 0755); } - len = (PATH_MAX - 3) - (count * XX_DIR_LEN); + len = (PATH_MAX - 3 - strlen(cwd)) - (count * XX_DIR_LEN); if (len <= 0) len = 1; memset(longname, 'y', len); longname[len] = '\0'; strcat(longpath, longname); + free(cwd); } exe_cp(src, longpath); @@ -190,7 +200,7 @@ static int check_execveat_pathmax(int dot_dfd, const char *src, int is_script) } /* - * Execute as a long pathname relative to ".". If this is a script, + * Execute as a long pathname relative to "/". If this is a script, * the interpreter will launch but fail to open the script because its * name ("/dev/fd/5/xxx....") is bigger than PATH_MAX. * @@ -200,10 +210,10 @@ static int check_execveat_pathmax(int dot_dfd, const char *src, int is_script) * the exit status shall be 126."), so allow either. */ if (is_script) - fail += check_execveat_invoked_rc(dot_dfd, longpath, 0, + fail += check_execveat_invoked_rc(root_dfd, longpath + 1, 0, 127, 126); else - fail += check_execveat(dot_dfd, longpath, 0); + fail += check_execveat(root_dfd, longpath + 1, 0); return fail; } @@ -218,6 +228,7 @@ static int run_tests(void) int subdir_dfd_ephemeral = open_or_die("subdir.ephemeral", O_DIRECTORY|O_RDONLY); int dot_dfd = open_or_die(".", O_DIRECTORY|O_RDONLY); + int root_dfd = open_or_die("/", O_DIRECTORY|O_RDONLY); int dot_dfd_path = open_or_die(".", O_DIRECTORY|O_RDONLY|O_PATH); int dot_dfd_cloexec = open_or_die(".", O_DIRECTORY|O_RDONLY|O_CLOEXEC); int fd = open_or_die("execveat", O_RDONLY); @@ -353,8 +364,8 @@ static int run_tests(void) /* Attempt to execute relative to non-directory => ENOTDIR */ fail += check_execveat_fail(fd, "execveat", 0, ENOTDIR); - fail += check_execveat_pathmax(dot_dfd, "execveat", 0); - fail += check_execveat_pathmax(dot_dfd, "script", 1); + fail += check_execveat_pathmax(root_dfd, "execveat", 0); + fail += check_execveat_pathmax(root_dfd, "script", 1); return fail; } diff --git a/tools/testing/selftests/firmware/fw_fallback.sh b/tools/testing/selftests/firmware/fw_fallback.sh index a52a3bab532b..34a42c68ebfb 100755 --- a/tools/testing/selftests/firmware/fw_fallback.sh +++ b/tools/testing/selftests/firmware/fw_fallback.sh @@ -86,6 +86,11 @@ load_fw_cancel() load_fw_custom() { + if [ ! -e "$DIR"/trigger_custom_fallback ]; then + echo "$0: custom fallback trigger not present, ignoring test" >&2 + return 1 + fi + local name="$1" local file="$2" @@ -108,11 +113,17 @@ load_fw_custom() # Wait for request to finish. wait + return 0 } load_fw_custom_cancel() { + if [ ! -e "$DIR"/trigger_custom_fallback ]; then + echo "$0: canceling custom fallback trigger not present, ignoring test" >&2 + return 1 + fi + local name="$1" local file="$2" @@ -133,6 +144,7 @@ load_fw_custom_cancel() # Wait for request to finish. wait + return 0 } load_fw_fallback_with_child() @@ -227,20 +239,22 @@ else echo "$0: cancelling fallback mechanism works" fi -load_fw_custom "$NAME" "$FW" -if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then - echo "$0: firmware was not loaded" >&2 - exit 1 -else - echo "$0: custom fallback loading mechanism works" +if load_fw_custom "$NAME" "$FW" ; then + if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then + echo "$0: firmware was not loaded" >&2 + exit 1 + else + echo "$0: custom fallback loading mechanism works" + fi fi -load_fw_custom_cancel "nope-$NAME" "$FW" -if diff -q "$FW" /dev/test_firmware >/dev/null ; then - echo "$0: firmware was expected to be cancelled" >&2 - exit 1 -else - echo "$0: cancelling custom fallback mechanism works" +if load_fw_custom_cancel "nope-$NAME" "$FW" ; then + if diff -q "$FW" /dev/test_firmware >/dev/null ; then + echo "$0: firmware was expected to be cancelled" >&2 + exit 1 + else + echo "$0: cancelling custom fallback mechanism works" + fi fi set +e diff --git a/tools/testing/selftests/firmware/fw_filesystem.sh b/tools/testing/selftests/firmware/fw_filesystem.sh index 62f2d6f54929..b1f20fef36c7 100755 --- a/tools/testing/selftests/firmware/fw_filesystem.sh +++ b/tools/testing/selftests/firmware/fw_filesystem.sh @@ -70,9 +70,13 @@ if printf '\000' >"$DIR"/trigger_request 2> /dev/null; then exit 1 fi -if printf '\000' >"$DIR"/trigger_async_request 2> /dev/null; then - echo "$0: empty filename should not succeed (async)" >&2 - exit 1 +if [ ! -e "$DIR"/trigger_async_request ]; then + echo "$0: empty filename: async trigger not present, ignoring test" >&2 +else + if printf '\000' >"$DIR"/trigger_async_request 2> /dev/null; then + echo "$0: empty filename should not succeed (async)" >&2 + exit 1 + fi fi # Request a firmware that doesn't exist, it should fail. @@ -105,17 +109,21 @@ else fi # Try the asynchronous version too -if ! echo -n "$NAME" >"$DIR"/trigger_async_request ; then - echo "$0: could not trigger async request" >&2 - exit 1 -fi - -# Verify the contents are what we expect. -if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then - echo "$0: firmware was not loaded (async)" >&2 - exit 1 +if [ ! -e "$DIR"/trigger_async_request ]; then + echo "$0: firmware loading: async trigger not present, ignoring test" >&2 else - echo "$0: async filesystem loading works" + if ! echo -n "$NAME" >"$DIR"/trigger_async_request ; then + echo "$0: could not trigger async request" >&2 + exit 1 + fi + + # Verify the contents are what we expect. + if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then + echo "$0: firmware was not loaded (async)" >&2 + exit 1 + else + echo "$0: async filesystem loading works" + fi fi ### Batched requests tests diff --git a/tools/testing/selftests/ftrace/config b/tools/testing/selftests/ftrace/config index 8a1c9f949fe0..b01924c71c09 100644 --- a/tools/testing/selftests/ftrace/config +++ b/tools/testing/selftests/ftrace/config @@ -1,2 +1,6 @@ CONFIG_KPROBES=y CONFIG_FTRACE=y +CONFIG_FUNCTION_PROFILER=y +CONFIG_TRACER_SNAPSHOT=y +CONFIG_STACK_TRACER=y +CONFIG_HIST_TRIGGERS=y diff --git a/tools/testing/selftests/ftrace/ftracetest b/tools/testing/selftests/ftrace/ftracetest index abc706cf7702..f9a9d424c980 100755 --- a/tools/testing/selftests/ftrace/ftracetest +++ b/tools/testing/selftests/ftrace/ftracetest @@ -222,7 +222,14 @@ SIG_RESULT= SIG_BASE=36 # Use realtime signals SIG_PID=$$ +exit_pass () { + exit 0 +} + SIG_FAIL=$((SIG_BASE + FAIL)) +exit_fail () { + exit 1 +} trap 'SIG_RESULT=$FAIL' $SIG_FAIL SIG_UNRESOLVED=$((SIG_BASE + UNRESOLVED)) diff --git a/tools/testing/selftests/ftrace/test.d/00basic/basic4.tc b/tools/testing/selftests/ftrace/test.d/00basic/basic4.tc index aa51f6c17359..0696098d6408 100644 --- a/tools/testing/selftests/ftrace/test.d/00basic/basic4.tc +++ b/tools/testing/selftests/ftrace/test.d/00basic/basic4.tc @@ -2,4 +2,4 @@ # description: Basic event tracing check test -f available_events -a -f set_event -a -d events # check scheduler events are available -grep -q sched available_events && exit 0 || exit $FAIL +grep -q sched available_events && exit_pass || exit_fail diff --git a/tools/testing/selftests/ftrace/test.d/event/event-enable.tc b/tools/testing/selftests/ftrace/test.d/event/event-enable.tc index 6ff851a75884..9daf034186f5 100644 --- a/tools/testing/selftests/ftrace/test.d/event/event-enable.tc +++ b/tools/testing/selftests/ftrace/test.d/event/event-enable.tc @@ -11,7 +11,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } yield() { diff --git a/tools/testing/selftests/ftrace/test.d/event/event-pid.tc b/tools/testing/selftests/ftrace/test.d/event/event-pid.tc index cc14feec6e1f..132478b305c2 100644 --- a/tools/testing/selftests/ftrace/test.d/event/event-pid.tc +++ b/tools/testing/selftests/ftrace/test.d/event/event-pid.tc @@ -13,7 +13,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } yield() { diff --git a/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc b/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc index 85094904aa79..6a37a8642ee6 100644 --- a/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc +++ b/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc @@ -11,7 +11,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } yield() { diff --git a/tools/testing/selftests/ftrace/test.d/event/toplevel-enable.tc b/tools/testing/selftests/ftrace/test.d/event/toplevel-enable.tc index cc1cf4d30ef5..4e9b6e2c0219 100644 --- a/tools/testing/selftests/ftrace/test.d/event/toplevel-enable.tc +++ b/tools/testing/selftests/ftrace/test.d/event/toplevel-enable.tc @@ -10,7 +10,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } yield() { diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter-stack.tc b/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter-stack.tc index 45df747887e0..1aec99d108eb 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter-stack.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter-stack.tc @@ -28,7 +28,7 @@ do_reset() { fail() { # msg do_reset echo $1 - exit $FAIL + exit_fail } disable_tracing diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter.tc b/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter.tc index 0387e22e7577..9f8d27ca39cf 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter.tc @@ -18,7 +18,7 @@ do_reset() { fail() { # msg do_reset echo $1 - exit $FAIL + exit_fail } disable_tracing diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-pid.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-pid.tc index 78524fcc25ae..524ce24b3c22 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-pid.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-pid.tc @@ -51,7 +51,7 @@ do_reset() { fail() { # msg do_reset echo $1 - exit $FAIL + exit_fail } yield() { diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc index 9d4afcca1e36..6fed4cf2db81 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc @@ -27,7 +27,7 @@ do_reset() { fail() { # mesg do_reset echo $1 - exit $FAIL + exit_fail } SLEEP_TIME=".1" @@ -48,8 +48,7 @@ test_event_enabled() { e=`cat $EVENT_ENABLE` if [ "$e" != $val ]; then - echo "Expected $val but found $e" - exit 1 + fail "Expected $val but found $e" fi } diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func_profiler.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func_profiler.tc index fe0dc5a7ea26..b2d5a8febfe8 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func_profiler.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func_profiler.tc @@ -32,7 +32,7 @@ fail() { # mesg reset_tracer echo > set_ftrace_filter echo $1 - exit $FAIL + exit_fail } echo "Testing function tracer with profiler:" diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func_set_ftrace_file.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func_set_ftrace_file.tc index 5ad723724adb..0f3f92622e33 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func_set_ftrace_file.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func_set_ftrace_file.tc @@ -26,14 +26,14 @@ do_reset() { fail() { # mesg do_reset echo $1 - exit $FAIL + exit_fail } do_reset FILTER=set_ftrace_filter FUNC1="schedule" -FUNC2="do_IRQ" +FUNC2="do_softirq" ALL_FUNCS="#### all functions enabled ####" diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func_traceonoff_triggers.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func_traceonoff_triggers.tc index cdc92a371cd7..f6d9ac73268a 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func_traceonoff_triggers.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func_traceonoff_triggers.tc @@ -27,7 +27,7 @@ do_reset() { fail() { # mesg do_reset echo $1 - exit $FAIL + exit_fail } SLEEP_TIME=".1" diff --git a/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc b/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc index d7f48b55df51..4fa0f79144f4 100644 --- a/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc +++ b/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc @@ -11,7 +11,7 @@ fail() { # mesg rmdir foo 2>/dev/null echo $1 set -e - exit $FAIL + exit_fail } cd instances diff --git a/tools/testing/selftests/ftrace/test.d/instances/instance.tc b/tools/testing/selftests/ftrace/test.d/instances/instance.tc index ddda62203366..b84651283bf3 100644 --- a/tools/testing/selftests/ftrace/test.d/instances/instance.tc +++ b/tools/testing/selftests/ftrace/test.d/instances/instance.tc @@ -11,7 +11,7 @@ fail() { # mesg rmdir x y z 2>/dev/null echo $1 set -e - exit $FAIL + exit_fail } cd instances diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/busy_check.tc b/tools/testing/selftests/ftrace/test.d/kprobe/busy_check.tc index 0e6f415c6152..bbc443a9190c 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/busy_check.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/busy_check.tc @@ -9,7 +9,7 @@ echo > kprobe_events echo p:myevent _do_fork > kprobe_events test -d events/kprobes/myevent echo 1 > events/kprobes/myevent/enable -echo > kprobe_events && exit 1 # this must fail +echo > kprobe_events && exit_fail # this must fail echo 0 > events/kprobes/myevent/enable echo > kprobe_events # this must succeed clear_trace diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args.tc index 679bbd23bcc3..8b43c6804fc3 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args.tc @@ -14,5 +14,5 @@ echo 1 > events/kprobes/testprobe/enable echo 0 > events/kprobes/testprobe/enable echo "-:testprobe" >> kprobe_events clear_trace -test -d events/kprobes/testprobe && exit 1 || exit 0 +test -d events/kprobes/testprobe && exit_fail || exit_pass diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_type.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_type.tc index 17d33ba192f6..2a1755bfc290 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_type.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_type.tc @@ -35,4 +35,4 @@ check_types $ARGS echo "-:testprobe" >> kprobe_events clear_trace -test -d events/kprobes/testprobe && exit 1 || exit 0 +test -d events/kprobes/testprobe && exit_fail || exit_pass diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_args.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_args.tc index f1825bdbe3f3..321954683aaa 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_args.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_args.tc @@ -14,4 +14,4 @@ echo 1 > events/kprobes/testprobe2/enable echo 0 > events/kprobes/testprobe2/enable echo '-:testprobe2' >> kprobe_events clear_trace -test -d events/kprobes/testprobe2 && exit 1 || exit 0 +test -d events/kprobes/testprobe2 && exit_fail || exit_pass diff --git a/tools/testing/selftests/ftrace/test.d/template b/tools/testing/selftests/ftrace/test.d/template index 5448f7abad5f..5c39ceb18a0d 100644 --- a/tools/testing/selftests/ftrace/test.d/template +++ b/tools/testing/selftests/ftrace/test.d/template @@ -4,6 +4,7 @@ # Note that all tests are run with "errexit" option. exit 0 # Return 0 if the test is passed, otherwise return !0 +# Or you can call exit_pass for passed test, and exit_fail for failed test. # If the test could not run because of lack of feature, call exit_unsupported # If the test returned unclear results, call exit_unresolved # If the test is a dummy, or a placeholder, call exit_untested diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-eventonoff.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-eventonoff.tc index 839ac4320b24..28cc355a3a7b 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-eventonoff.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-eventonoff.tc @@ -12,7 +12,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } if [ ! -f set_event -o ! -d events/sched ]; then diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-filter.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-filter.tc index 66873c4b12c9..a48e23eb8a8b 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-filter.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-filter.tc @@ -12,7 +12,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } if [ ! -f set_event -o ! -d events/sched ]; then diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-mod.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-mod.tc index 4237b32769f1..8da80efc44d8 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-mod.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-mod.tc @@ -12,7 +12,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } if [ ! -f set_event -o ! -d events/sched ]; then diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist.tc index d24e2b8bd863..449fe9ff91a2 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist.tc @@ -12,7 +12,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } if [ ! -f set_event -o ! -d events/sched ]; then diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-multihist.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-multihist.tc index 4c0774fff378..c5ef8b9d02b3 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-multihist.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-multihist.tc @@ -12,7 +12,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } if [ ! -f set_event -o ! -d events/sched ]; then diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-snapshot.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-snapshot.tc index 3fc6321e081f..ed38f0050d77 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-snapshot.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-snapshot.tc @@ -11,7 +11,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } if [ ! -f set_event -o ! -d events/sched ]; then diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-stacktrace.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-stacktrace.tc index 3652824f81ed..3121d795a868 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-stacktrace.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-stacktrace.tc @@ -11,7 +11,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } if [ ! -f set_event -o ! -d events/sched ]; then diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-traceonoff.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-traceonoff.tc index 6d9051cdf408..c59d9eb546da 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-traceonoff.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-traceonoff.tc @@ -11,7 +11,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } if [ ! -f set_event -o ! -d events/sched ]; then diff --git a/tools/testing/selftests/memfd/memfd_test.c b/tools/testing/selftests/memfd/memfd_test.c index 845e5f67b6f0..132a54f74e88 100644 --- a/tools/testing/selftests/memfd/memfd_test.c +++ b/tools/testing/selftests/memfd/memfd_test.c @@ -515,7 +515,7 @@ static void mfd_assert_grow_write(int fd) buf = malloc(mfd_def_size * 8); if (!buf) { - printf("malloc(%d) failed: %m\n", mfd_def_size * 8); + printf("malloc(%zu) failed: %m\n", mfd_def_size * 8); abort(); } @@ -535,7 +535,7 @@ static void mfd_fail_grow_write(int fd) buf = malloc(mfd_def_size * 8); if (!buf) { - printf("malloc(%d) failed: %m\n", mfd_def_size * 8); + printf("malloc(%zu) failed: %m\n", mfd_def_size * 8); abort(); } diff --git a/tools/testing/selftests/memory-hotplug/Makefile b/tools/testing/selftests/memory-hotplug/Makefile index 23db11c94b59..86636d207adf 100644 --- a/tools/testing/selftests/memory-hotplug/Makefile +++ b/tools/testing/selftests/memory-hotplug/Makefile @@ -4,10 +4,10 @@ all: include ../lib.mk TEST_PROGS := mem-on-off-test.sh -override RUN_TESTS := ./mem-on-off-test.sh -r 2 || echo "selftests: memory-hotplug [FAIL]" +override RUN_TESTS := ./mem-on-off-test.sh -r 2 && echo "selftests: memory-hotplug [PASS]" || echo "selftests: memory-hotplug [FAIL]" override EMIT_TESTS := echo "$(RUN_TESTS)" run_full_test: - @/bin/bash ./mem-on-off-test.sh || echo "memory-hotplug selftests: [FAIL]" + @/bin/bash ./mem-on-off-test.sh && echo "memory-hotplug selftests: [PASS]" || echo "memory-hotplug selftests: [FAIL]" clean: diff --git a/tools/testing/selftests/powerpc/benchmarks/context_switch.c b/tools/testing/selftests/powerpc/benchmarks/context_switch.c index f4241339edd2..87f1f0252299 100644 --- a/tools/testing/selftests/powerpc/benchmarks/context_switch.c +++ b/tools/testing/selftests/powerpc/benchmarks/context_switch.c @@ -10,6 +10,7 @@ */ #define _GNU_SOURCE +#include <errno.h> #include <sched.h> #include <string.h> #include <stdio.h> @@ -75,6 +76,7 @@ static void touch(void) static void start_thread_on(void *(*fn)(void *), void *arg, unsigned long cpu) { + int rc; pthread_t tid; cpu_set_t cpuset; pthread_attr_t attr; @@ -82,14 +84,23 @@ static void start_thread_on(void *(*fn)(void *), void *arg, unsigned long cpu) CPU_ZERO(&cpuset); CPU_SET(cpu, &cpuset); - pthread_attr_init(&attr); + rc = pthread_attr_init(&attr); + if (rc) { + errno = rc; + perror("pthread_attr_init"); + exit(1); + } - if (pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset)) { + rc = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset); + if (rc) { + errno = rc; perror("pthread_attr_setaffinity_np"); exit(1); } - if (pthread_create(&tid, &attr, fn, arg)) { + rc = pthread_create(&tid, &attr, fn, arg); + if (rc) { + errno = rc; perror("pthread_create"); exit(1); } diff --git a/tools/testing/selftests/powerpc/dscr/dscr.h b/tools/testing/selftests/powerpc/dscr/dscr.h index 18ea223bd398..cdb840bc54f2 100644 --- a/tools/testing/selftests/powerpc/dscr/dscr.h +++ b/tools/testing/selftests/powerpc/dscr/dscr.h @@ -39,7 +39,7 @@ #define rmb() asm volatile("lwsync":::"memory") #define wmb() asm volatile("lwsync":::"memory") -#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x)) +#define READ_ONCE(x) (*(volatile typeof(x) *)&(x)) /* Prilvilege state DSCR access */ inline unsigned long get_dscr(void) diff --git a/tools/testing/selftests/powerpc/dscr/dscr_default_test.c b/tools/testing/selftests/powerpc/dscr/dscr_default_test.c index df17c3bab0a7..9e1a37e93b63 100644 --- a/tools/testing/selftests/powerpc/dscr/dscr_default_test.c +++ b/tools/testing/selftests/powerpc/dscr/dscr_default_test.c @@ -27,7 +27,7 @@ static void *do_test(void *in) unsigned long d, cur_dscr, cur_dscr_usr; unsigned long s1, s2; - s1 = ACCESS_ONCE(sequence); + s1 = READ_ONCE(sequence); if (s1 & 1) continue; rmb(); diff --git a/tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c b/tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c index 17fb1b43c320..1899bd85121f 100644 --- a/tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c +++ b/tools/testing/selftests/powerpc/dscr/dscr_sysfs_test.c @@ -53,6 +53,8 @@ static int check_all_cpu_dscr_defaults(unsigned long val) } while ((dp = readdir(sysfs))) { + int len; + if (!(dp->d_type & DT_DIR)) continue; if (!strcmp(dp->d_name, "cpuidle")) @@ -60,7 +62,9 @@ static int check_all_cpu_dscr_defaults(unsigned long val) if (!strstr(dp->d_name, "cpu")) continue; - sprintf(file, "%s%s/dscr", CPU_PATH, dp->d_name); + len = snprintf(file, LEN_MAX, "%s%s/dscr", CPU_PATH, dp->d_name); + if (len >= LEN_MAX) + continue; if (access(file, F_OK)) continue; diff --git a/tools/testing/selftests/powerpc/tm/.gitignore b/tools/testing/selftests/powerpc/tm/.gitignore index 2f1f7b013293..241a4a4ee0e4 100644 --- a/tools/testing/selftests/powerpc/tm/.gitignore +++ b/tools/testing/selftests/powerpc/tm/.gitignore @@ -12,3 +12,4 @@ tm-signal-context-chk-gpr tm-signal-context-chk-vmx tm-signal-context-chk-vsx tm-vmx-unavail +tm-unavailable diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile index fca7c7f5e640..8ed6f8c57230 100644 --- a/tools/testing/selftests/powerpc/tm/Makefile +++ b/tools/testing/selftests/powerpc/tm/Makefile @@ -3,7 +3,7 @@ SIGNAL_CONTEXT_CHK_TESTS := tm-signal-context-chk-gpr tm-signal-context-chk-fpu tm-signal-context-chk-vmx tm-signal-context-chk-vsx TEST_GEN_PROGS := tm-resched-dscr tm-syscall tm-signal-msr-resv tm-signal-stack \ - tm-vmxcopy tm-fork tm-tar tm-tmspr tm-vmx-unavail \ + tm-vmxcopy tm-fork tm-tar tm-tmspr tm-vmx-unavail tm-unavailable \ $(SIGNAL_CONTEXT_CHK_TESTS) include ../../lib.mk @@ -17,6 +17,7 @@ $(OUTPUT)/tm-syscall: CFLAGS += -I../../../../../usr/include $(OUTPUT)/tm-tmspr: CFLAGS += -pthread $(OUTPUT)/tm-vmx-unavail: CFLAGS += -pthread -m64 $(OUTPUT)/tm-resched-dscr: ../pmu/lib.o +$(OUTPUT)/tm-unavailable: CFLAGS += -O0 -pthread -m64 -Wno-error=uninitialized -mvsx SIGNAL_CONTEXT_CHK_TESTS := $(patsubst %,$(OUTPUT)/%,$(SIGNAL_CONTEXT_CHK_TESTS)) $(SIGNAL_CONTEXT_CHK_TESTS): tm-signal.S diff --git a/tools/testing/selftests/powerpc/tm/tm-unavailable.c b/tools/testing/selftests/powerpc/tm/tm-unavailable.c new file mode 100644 index 000000000000..96c37f84ce54 --- /dev/null +++ b/tools/testing/selftests/powerpc/tm/tm-unavailable.c @@ -0,0 +1,371 @@ +/* + * Copyright 2017, Gustavo Romero, Breno Leitao, Cyril Bur, IBM Corp. + * Licensed under GPLv2. + * + * Force FP, VEC and VSX unavailable exception during transaction in all + * possible scenarios regarding the MSR.FP and MSR.VEC state, e.g. when FP + * is enable and VEC is disable, when FP is disable and VEC is enable, and + * so on. Then we check if the restored state is correctly set for the + * FP and VEC registers to the previous state we set just before we entered + * in TM, i.e. we check if it corrupts somehow the recheckpointed FP and + * VEC/Altivec registers on abortion due to an unavailable exception in TM. + * N.B. In this test we do not test all the FP/Altivec/VSX registers for + * corruption, but only for registers vs0 and vs32, which are respectively + * representatives of FP and VEC/Altivec reg sets. + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <inttypes.h> +#include <stdbool.h> +#include <pthread.h> +#include <sched.h> + +#include "tm.h" + +#define DEBUG 0 + +/* Unavailable exceptions to test in HTM */ +#define FP_UNA_EXCEPTION 0 +#define VEC_UNA_EXCEPTION 1 +#define VSX_UNA_EXCEPTION 2 + +#define NUM_EXCEPTIONS 3 + +struct Flags { + int touch_fp; + int touch_vec; + int result; + int exception; +} flags; + +bool expecting_failure(void) +{ + if (flags.touch_fp && flags.exception == FP_UNA_EXCEPTION) + return false; + + if (flags.touch_vec && flags.exception == VEC_UNA_EXCEPTION) + return false; + + /* + * If both FP and VEC are touched it does not mean that touching VSX + * won't raise an exception. However since FP and VEC state are already + * correctly loaded, the transaction is not aborted (i.e. + * treclaimed/trecheckpointed) and MSR.VSX is just set as 1, so a TM + * failure is not expected also in this case. + */ + if ((flags.touch_fp && flags.touch_vec) && + flags.exception == VSX_UNA_EXCEPTION) + return false; + + return true; +} + +/* Check if failure occurred whilst in transaction. */ +bool is_failure(uint64_t condition_reg) +{ + /* + * When failure handling occurs, CR0 is set to 0b1010 (0xa). Otherwise + * transaction completes without failure and hence reaches out 'tend.' + * that sets CR0 to 0b0100 (0x4). + */ + return ((condition_reg >> 28) & 0xa) == 0xa; +} + +void *ping(void *input) +{ + + /* + * Expected values for vs0 and vs32 after a TM failure. They must never + * change, otherwise they got corrupted. + */ + uint64_t high_vs0 = 0x5555555555555555; + uint64_t low_vs0 = 0xffffffffffffffff; + uint64_t high_vs32 = 0x5555555555555555; + uint64_t low_vs32 = 0xffffffffffffffff; + + /* Counter for busy wait */ + uint64_t counter = 0x1ff000000; + + /* + * Variable to keep a copy of CR register content taken just after we + * leave the transactional state. + */ + uint64_t cr_ = 0; + + /* + * Wait a bit so thread can get its name "ping". This is not important + * to reproduce the issue but it's nice to have for systemtap debugging. + */ + if (DEBUG) + sleep(1); + + printf("If MSR.FP=%d MSR.VEC=%d: ", flags.touch_fp, flags.touch_vec); + + if (flags.exception != FP_UNA_EXCEPTION && + flags.exception != VEC_UNA_EXCEPTION && + flags.exception != VSX_UNA_EXCEPTION) { + printf("No valid exception specified to test.\n"); + return NULL; + } + + asm ( + /* Prepare to merge low and high. */ + " mtvsrd 33, %[high_vs0] ;" + " mtvsrd 34, %[low_vs0] ;" + + /* + * Adjust VS0 expected value after an TM failure, + * i.e. vs0 = 0x5555555555555555555FFFFFFFFFFFFFFFF + */ + " xxmrghd 0, 33, 34 ;" + + /* + * Adjust VS32 expected value after an TM failure, + * i.e. vs32 = 0x5555555555555555555FFFFFFFFFFFFFFFF + */ + " xxmrghd 32, 33, 34 ;" + + /* + * Wait an amount of context switches so load_fp and load_vec + * overflow and MSR.FP, MSR.VEC, and MSR.VSX become zero (off). + */ + " mtctr %[counter] ;" + + /* Decrement CTR branch if CTR non zero. */ + "1: bdnz 1b ;" + + /* + * Check if we want to touch FP prior to the test in order + * to set MSR.FP = 1 before provoking an unavailable + * exception in TM. + */ + " cmpldi %[touch_fp], 0 ;" + " beq no_fp ;" + " fadd 10, 10, 10 ;" + "no_fp: ;" + + /* + * Check if we want to touch VEC prior to the test in order + * to set MSR.VEC = 1 before provoking an unavailable + * exception in TM. + */ + " cmpldi %[touch_vec], 0 ;" + " beq no_vec ;" + " vaddcuw 10, 10, 10 ;" + "no_vec: ;" + + /* + * Perhaps it would be a better idea to do the + * compares outside transactional context and simply + * duplicate code. + */ + " tbegin. ;" + " beq trans_fail ;" + + /* Do we do FP Unavailable? */ + " cmpldi %[exception], %[ex_fp] ;" + " bne 1f ;" + " fadd 10, 10, 10 ;" + " b done ;" + + /* Do we do VEC Unavailable? */ + "1: cmpldi %[exception], %[ex_vec] ;" + " bne 2f ;" + " vaddcuw 10, 10, 10 ;" + " b done ;" + + /* + * Not FP or VEC, therefore VSX. Ensure this + * instruction always generates a VSX Unavailable. + * ISA 3.0 is tricky here. + * (xxmrghd will on ISA 2.07 and ISA 3.0) + */ + "2: xxmrghd 10, 10, 10 ;" + + "done: tend. ;" + + "trans_fail: ;" + + /* Give values back to C. */ + " mfvsrd %[high_vs0], 0 ;" + " xxsldwi 3, 0, 0, 2 ;" + " mfvsrd %[low_vs0], 3 ;" + " mfvsrd %[high_vs32], 32 ;" + " xxsldwi 3, 32, 32, 2 ;" + " mfvsrd %[low_vs32], 3 ;" + + /* Give CR back to C so that it can check what happened. */ + " mfcr %[cr_] ;" + + : [high_vs0] "+r" (high_vs0), + [low_vs0] "+r" (low_vs0), + [high_vs32] "=r" (high_vs32), + [low_vs32] "=r" (low_vs32), + [cr_] "+r" (cr_) + : [touch_fp] "r" (flags.touch_fp), + [touch_vec] "r" (flags.touch_vec), + [exception] "r" (flags.exception), + [ex_fp] "i" (FP_UNA_EXCEPTION), + [ex_vec] "i" (VEC_UNA_EXCEPTION), + [ex_vsx] "i" (VSX_UNA_EXCEPTION), + [counter] "r" (counter) + + : "cr0", "ctr", "v10", "vs0", "vs10", "vs3", "vs32", "vs33", + "vs34", "fr10" + + ); + + /* + * Check if we were expecting a failure and it did not occur by checking + * CR0 state just after we leave the transaction. Either way we check if + * vs0 or vs32 got corrupted. + */ + if (expecting_failure() && !is_failure(cr_)) { + printf("\n\tExpecting the transaction to fail, %s", + "but it didn't\n\t"); + flags.result++; + } + + /* Check if we were not expecting a failure and a it occurred. */ + if (!expecting_failure() && is_failure(cr_)) { + printf("\n\tUnexpected transaction failure 0x%02lx\n\t", + failure_code()); + return (void *) -1; + } + + /* + * Check if TM failed due to the cause we were expecting. 0xda is a + * TM_CAUSE_FAC_UNAV cause, otherwise it's an unexpected cause. + */ + if (is_failure(cr_) && !failure_is_unavailable()) { + printf("\n\tUnexpected failure cause 0x%02lx\n\t", + failure_code()); + return (void *) -1; + } + + /* 0x4 is a success and 0xa is a fail. See comment in is_failure(). */ + if (DEBUG) + printf("CR0: 0x%1lx ", cr_ >> 28); + + /* Check FP (vs0) for the expected value. */ + if (high_vs0 != 0x5555555555555555 || low_vs0 != 0xFFFFFFFFFFFFFFFF) { + printf("FP corrupted!"); + printf(" high = %#16" PRIx64 " low = %#16" PRIx64 " ", + high_vs0, low_vs0); + flags.result++; + } else + printf("FP ok "); + + /* Check VEC (vs32) for the expected value. */ + if (high_vs32 != 0x5555555555555555 || low_vs32 != 0xFFFFFFFFFFFFFFFF) { + printf("VEC corrupted!"); + printf(" high = %#16" PRIx64 " low = %#16" PRIx64, + high_vs32, low_vs32); + flags.result++; + } else + printf("VEC ok"); + + putchar('\n'); + + return NULL; +} + +/* Thread to force context switch */ +void *pong(void *not_used) +{ + /* Wait thread get its name "pong". */ + if (DEBUG) + sleep(1); + + /* Classed as an interactive-like thread. */ + while (1) + sched_yield(); +} + +/* Function that creates a thread and launches the "ping" task. */ +void test_fp_vec(int fp, int vec, pthread_attr_t *attr) +{ + int retries = 2; + void *ret_value; + pthread_t t0; + + flags.touch_fp = fp; + flags.touch_vec = vec; + + /* + * Without luck it's possible that the transaction is aborted not due to + * the unavailable exception caught in the middle as we expect but also, + * for instance, due to a context switch or due to a KVM reschedule (if + * it's running on a VM). Thus we try a few times before giving up, + * checking if the failure cause is the one we expect. + */ + do { + /* Bind 'ping' to CPU 0, as specified in 'attr'. */ + pthread_create(&t0, attr, ping, (void *) &flags); + pthread_setname_np(t0, "ping"); + pthread_join(t0, &ret_value); + retries--; + } while (ret_value != NULL && retries); + + if (!retries) { + flags.result = 1; + if (DEBUG) + printf("All transactions failed unexpectedly\n"); + + } +} + +int main(int argc, char **argv) +{ + int exception; /* FP = 0, VEC = 1, VSX = 2 */ + pthread_t t1; + pthread_attr_t attr; + cpu_set_t cpuset; + + /* Set only CPU 0 in the mask. Both threads will be bound to CPU 0. */ + CPU_ZERO(&cpuset); + CPU_SET(0, &cpuset); + + /* Init pthread attribute. */ + pthread_attr_init(&attr); + + /* Set CPU 0 mask into the pthread attribute. */ + pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset); + + pthread_create(&t1, &attr /* Bind 'pong' to CPU 0 */, pong, NULL); + pthread_setname_np(t1, "pong"); /* Name it for systemtap convenience */ + + flags.result = 0; + + for (exception = 0; exception < NUM_EXCEPTIONS; exception++) { + printf("Checking if FP/VEC registers are sane after"); + + if (exception == FP_UNA_EXCEPTION) + printf(" a FP unavailable exception...\n"); + + else if (exception == VEC_UNA_EXCEPTION) + printf(" a VEC unavailable exception...\n"); + + else + printf(" a VSX unavailable exception...\n"); + + flags.exception = exception; + + test_fp_vec(0, 0, &attr); + test_fp_vec(1, 0, &attr); + test_fp_vec(0, 1, &attr); + test_fp_vec(1, 1, &attr); + + } + + if (flags.result > 0) { + printf("result: failed!\n"); + exit(1); + } else { + printf("result: success\n"); + exit(0); + } +} diff --git a/tools/testing/selftests/powerpc/tm/tm.h b/tools/testing/selftests/powerpc/tm/tm.h index 0ffff04433c5..df4204247d45 100644 --- a/tools/testing/selftests/powerpc/tm/tm.h +++ b/tools/testing/selftests/powerpc/tm/tm.h @@ -47,6 +47,11 @@ static inline bool failure_is_syscall(void) return (failure_code() & TM_CAUSE_SYSCALL) == TM_CAUSE_SYSCALL; } +static inline bool failure_is_unavailable(void) +{ + return (failure_code() & TM_CAUSE_FAC_UNAV) == TM_CAUSE_FAC_UNAV; +} + static inline bool failure_is_nesting(void) { return (__builtin_get_texasru() & 0x400000); diff --git a/tools/testing/selftests/rcutorture/bin/config_override.sh b/tools/testing/selftests/rcutorture/bin/config_override.sh index 49fa51726ce3..ef7fcbac3d42 100755 --- a/tools/testing/selftests/rcutorture/bin/config_override.sh +++ b/tools/testing/selftests/rcutorture/bin/config_override.sh @@ -42,7 +42,7 @@ else exit 1 fi -T=/tmp/config_override.sh.$$ +T=${TMPDIR-/tmp}/config_override.sh.$$ trap 'rm -rf $T' 0 mkdir $T diff --git a/tools/testing/selftests/rcutorture/bin/configcheck.sh b/tools/testing/selftests/rcutorture/bin/configcheck.sh index 70fca318a82b..197deece7c7c 100755 --- a/tools/testing/selftests/rcutorture/bin/configcheck.sh +++ b/tools/testing/selftests/rcutorture/bin/configcheck.sh @@ -19,7 +19,7 @@ # # Authors: Paul E. McKenney <paulmck@linux.vnet.ibm.com> -T=/tmp/abat-chk-config.sh.$$ +T=${TMPDIR-/tmp}/abat-chk-config.sh.$$ trap 'rm -rf $T' 0 mkdir $T diff --git a/tools/testing/selftests/rcutorture/bin/configinit.sh b/tools/testing/selftests/rcutorture/bin/configinit.sh index 3f81a1095206..51f66a7ce876 100755 --- a/tools/testing/selftests/rcutorture/bin/configinit.sh +++ b/tools/testing/selftests/rcutorture/bin/configinit.sh @@ -32,7 +32,7 @@ # # Authors: Paul E. McKenney <paulmck@linux.vnet.ibm.com> -T=/tmp/configinit.sh.$$ +T=${TMPDIR-/tmp}/configinit.sh.$$ trap 'rm -rf $T' 0 mkdir $T diff --git a/tools/testing/selftests/rcutorture/bin/kvm-build.sh b/tools/testing/selftests/rcutorture/bin/kvm-build.sh index 46752c164676..fb66d0173638 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-build.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-build.sh @@ -35,7 +35,7 @@ then exit 1 fi -T=/tmp/test-linux.sh.$$ +T=${TMPDIR-/tmp}/test-linux.sh.$$ trap 'rm -rf $T' 0 mkdir $T diff --git a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh index 0af36a721b9c..ab14b97c942c 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh @@ -38,7 +38,7 @@ # # Authors: Paul E. McKenney <paulmck@linux.vnet.ibm.com> -T=/tmp/kvm-test-1-run.sh.$$ +T=${TMPDIR-/tmp}/kvm-test-1-run.sh.$$ trap 'rm -rf $T' 0 mkdir $T diff --git a/tools/testing/selftests/rcutorture/bin/kvm.sh b/tools/testing/selftests/rcutorture/bin/kvm.sh index b55895fb10ed..ccd49e958fd2 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm.sh @@ -30,7 +30,7 @@ scriptname=$0 args="$*" -T=/tmp/kvm.sh.$$ +T=${TMPDIR-/tmp}/kvm.sh.$$ trap 'rm -rf $T' 0 mkdir $T @@ -222,7 +222,7 @@ do exit 1 fi done -sort -k2nr $T/cfgcpu > $T/cfgcpu.sort +sort -k2nr $T/cfgcpu -T="$T" > $T/cfgcpu.sort # Use a greedy bin-packing algorithm, sorting the list accordingly. awk < $T/cfgcpu.sort > $T/cfgcpu.pack -v ncpus=$cpus ' diff --git a/tools/testing/selftests/rcutorture/bin/parse-build.sh b/tools/testing/selftests/rcutorture/bin/parse-build.sh index a6b57622c2e5..24fe5f822b28 100755 --- a/tools/testing/selftests/rcutorture/bin/parse-build.sh +++ b/tools/testing/selftests/rcutorture/bin/parse-build.sh @@ -28,7 +28,7 @@ F=$1 title=$2 -T=/tmp/parse-build.sh.$$ +T=${TMPDIR-/tmp}/parse-build.sh.$$ trap 'rm -rf $T' 0 mkdir $T diff --git a/tools/testing/selftests/rcutorture/bin/parse-torture.sh b/tools/testing/selftests/rcutorture/bin/parse-torture.sh index e3c5f0705696..f12c38909b00 100755 --- a/tools/testing/selftests/rcutorture/bin/parse-torture.sh +++ b/tools/testing/selftests/rcutorture/bin/parse-torture.sh @@ -27,7 +27,7 @@ # # Authors: Paul E. McKenney <paulmck@linux.vnet.ibm.com> -T=/tmp/parse-torture.sh.$$ +T=${TMPDIR-/tmp}/parse-torture.sh.$$ file="$1" title="$2" diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/barriers.h b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/barriers.h index be3fdd351937..3f95a768a03b 100644 --- a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/barriers.h +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/src/barriers.h @@ -35,8 +35,7 @@ #define rs_smp_mb() do {} while (0) #endif -#define ACCESS_ONCE(x) (*(volatile typeof(x) *) &(x)) -#define READ_ONCE(x) ACCESS_ONCE(x) -#define WRITE_ONCE(x, val) (ACCESS_ONCE(x) = (val)) +#define READ_ONCE(x) (*(volatile typeof(x) *) &(x)) +#define WRITE_ONCE(x) ((*(volatile typeof(x) *) &(x)) = (val)) #endif diff --git a/tools/testing/selftests/seccomp/.gitignore b/tools/testing/selftests/seccomp/.gitignore index 346d83ca8069..5af29d3a1b0a 100644 --- a/tools/testing/selftests/seccomp/.gitignore +++ b/tools/testing/selftests/seccomp/.gitignore @@ -1 +1,2 @@ seccomp_bpf +seccomp_benchmark diff --git a/tools/testing/selftests/timers/.gitignore b/tools/testing/selftests/timers/.gitignore index cc986621f512..2c8ac8416299 100644 --- a/tools/testing/selftests/timers/.gitignore +++ b/tools/testing/selftests/timers/.gitignore @@ -18,3 +18,5 @@ threadtest valid-adjtimex adjtick set-tz +freq-step +rtctest_setdate diff --git a/tools/testing/selftests/vDSO/vdso_test.c b/tools/testing/selftests/vDSO/vdso_test.c index 8daeb7d7032c..2df26bd0099c 100644 --- a/tools/testing/selftests/vDSO/vdso_test.c +++ b/tools/testing/selftests/vDSO/vdso_test.c @@ -19,6 +19,19 @@ extern void *vdso_sym(const char *version, const char *name); extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); extern void vdso_init_from_auxv(void *auxv); +/* + * ARM64's vDSO exports its gettimeofday() implementation with a different + * name and version from other architectures, so we need to handle it as + * a special case. + */ +#if defined(__aarch64__) +const char *version = "LINUX_2.6.39"; +const char *name = "__kernel_gettimeofday"; +#else +const char *version = "LINUX_2.6"; +const char *name = "__vdso_gettimeofday"; +#endif + int main(int argc, char **argv) { unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR); @@ -31,10 +44,10 @@ int main(int argc, char **argv) /* Find gettimeofday. */ typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); - gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday"); + gtod_t gtod = (gtod_t)vdso_sym(version, name); if (!gtod) { - printf("Could not find __vdso_gettimeofday\n"); + printf("Could not find %s\n", name); return 1; } @@ -45,7 +58,7 @@ int main(int argc, char **argv) printf("The time is %lld.%06lld\n", (long long)tv.tv_sec, (long long)tv.tv_usec); } else { - printf("__vdso_gettimeofday failed\n"); + printf("%s failed\n", name); } return 0; diff --git a/tools/testing/selftests/vm/.gitignore b/tools/testing/selftests/vm/.gitignore index 142c565bb351..1ca2ee4d15b9 100644 --- a/tools/testing/selftests/vm/.gitignore +++ b/tools/testing/selftests/vm/.gitignore @@ -8,3 +8,5 @@ on-fault-limit transhuge-stress userfaultfd mlock-intersect-test +mlock-random-test +virtual_address_range diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile index e49eca1915f8..7f45806bd863 100644 --- a/tools/testing/selftests/vm/Makefile +++ b/tools/testing/selftests/vm/Makefile @@ -18,6 +18,7 @@ TEST_GEN_FILES += transhuge-stress TEST_GEN_FILES += userfaultfd TEST_GEN_FILES += mlock-random-test TEST_GEN_FILES += virtual_address_range +TEST_GEN_FILES += gup_benchmark TEST_PROGS := run_vmtests diff --git a/tools/testing/selftests/vm/gup_benchmark.c b/tools/testing/selftests/vm/gup_benchmark.c new file mode 100644 index 000000000000..36df55132036 --- /dev/null +++ b/tools/testing/selftests/vm/gup_benchmark.c @@ -0,0 +1,91 @@ +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/prctl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <linux/types.h> + +#define MB (1UL << 20) +#define PAGE_SIZE sysconf(_SC_PAGESIZE) + +#define GUP_FAST_BENCHMARK _IOWR('g', 1, struct gup_benchmark) + +struct gup_benchmark { + __u64 delta_usec; + __u64 addr; + __u64 size; + __u32 nr_pages_per_call; + __u32 flags; +}; + +int main(int argc, char **argv) +{ + struct gup_benchmark gup; + unsigned long size = 128 * MB; + int i, fd, opt, nr_pages = 1, thp = -1, repeats = 1, write = 0; + char *p; + + while ((opt = getopt(argc, argv, "m:r:n:tT")) != -1) { + switch (opt) { + case 'm': + size = atoi(optarg) * MB; + break; + case 'r': + repeats = atoi(optarg); + break; + case 'n': + nr_pages = atoi(optarg); + break; + case 't': + thp = 1; + break; + case 'T': + thp = 0; + break; + case 'w': + write = 1; + default: + return -1; + } + } + + gup.nr_pages_per_call = nr_pages; + gup.flags = write; + + fd = open("/sys/kernel/debug/gup_benchmark", O_RDWR); + if (fd == -1) + perror("open"), exit(1); + + p = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (p == MAP_FAILED) + perror("mmap"), exit(1); + gup.addr = (unsigned long)p; + + if (thp == 1) + madvise(p, size, MADV_HUGEPAGE); + else if (thp == 0) + madvise(p, size, MADV_NOHUGEPAGE); + + for (; (unsigned long)p < gup.addr + size; p += PAGE_SIZE) + p[0] = 0; + + for (i = 0; i < repeats; i++) { + gup.size = size; + if (ioctl(fd, GUP_FAST_BENCHMARK, &gup)) + perror("ioctl"), exit(1); + + printf("Time: %lld us", gup.delta_usec); + if (gup.size != size) + printf(", truncated (size: %lld)", gup.size); + printf("\n"); + } + + return 0; +} diff --git a/tools/testing/selftests/x86/entry_from_vm86.c b/tools/testing/selftests/x86/entry_from_vm86.c index d075ea0e5ca1..361466a2eaef 100644 --- a/tools/testing/selftests/x86/entry_from_vm86.c +++ b/tools/testing/selftests/x86/entry_from_vm86.c @@ -95,6 +95,27 @@ asm ( "int3\n\t" "vmcode_int80:\n\t" "int $0x80\n\t" + "vmcode_umip:\n\t" + /* addressing via displacements */ + "smsw (2052)\n\t" + "sidt (2054)\n\t" + "sgdt (2060)\n\t" + /* addressing via registers */ + "mov $2066, %bx\n\t" + "smsw (%bx)\n\t" + "mov $2068, %bx\n\t" + "sidt (%bx)\n\t" + "mov $2074, %bx\n\t" + "sgdt (%bx)\n\t" + /* register operands, only for smsw */ + "smsw %ax\n\t" + "mov %ax, (2080)\n\t" + "int3\n\t" + "vmcode_umip_str:\n\t" + "str %eax\n\t" + "vmcode_umip_sldt:\n\t" + "sldt %eax\n\t" + "int3\n\t" ".size vmcode, . - vmcode\n\t" "end_vmcode:\n\t" ".code32\n\t" @@ -103,7 +124,8 @@ asm ( extern unsigned char vmcode[], end_vmcode[]; extern unsigned char vmcode_bound[], vmcode_sysenter[], vmcode_syscall[], - vmcode_sti[], vmcode_int3[], vmcode_int80[]; + vmcode_sti[], vmcode_int3[], vmcode_int80[], vmcode_umip[], + vmcode_umip_str[], vmcode_umip_sldt[]; /* Returns false if the test was skipped. */ static bool do_test(struct vm86plus_struct *v86, unsigned long eip, @@ -160,6 +182,68 @@ static bool do_test(struct vm86plus_struct *v86, unsigned long eip, return true; } +void do_umip_tests(struct vm86plus_struct *vm86, unsigned char *test_mem) +{ + struct table_desc { + unsigned short limit; + unsigned long base; + } __attribute__((packed)); + + /* Initialize variables with arbitrary values */ + struct table_desc gdt1 = { .base = 0x3c3c3c3c, .limit = 0x9999 }; + struct table_desc gdt2 = { .base = 0x1a1a1a1a, .limit = 0xaeae }; + struct table_desc idt1 = { .base = 0x7b7b7b7b, .limit = 0xf1f1 }; + struct table_desc idt2 = { .base = 0x89898989, .limit = 0x1313 }; + unsigned short msw1 = 0x1414, msw2 = 0x2525, msw3 = 3737; + + /* UMIP -- exit with INT3 unless kernel emulation did not trap #GP */ + do_test(vm86, vmcode_umip - vmcode, VM86_TRAP, 3, "UMIP tests"); + + /* Results from displacement-only addressing */ + msw1 = *(unsigned short *)(test_mem + 2052); + memcpy(&idt1, test_mem + 2054, sizeof(idt1)); + memcpy(&gdt1, test_mem + 2060, sizeof(gdt1)); + + /* Results from register-indirect addressing */ + msw2 = *(unsigned short *)(test_mem + 2066); + memcpy(&idt2, test_mem + 2068, sizeof(idt2)); + memcpy(&gdt2, test_mem + 2074, sizeof(gdt2)); + + /* Results when using register operands */ + msw3 = *(unsigned short *)(test_mem + 2080); + + printf("[INFO]\tResult from SMSW:[0x%04x]\n", msw1); + printf("[INFO]\tResult from SIDT: limit[0x%04x]base[0x%08lx]\n", + idt1.limit, idt1.base); + printf("[INFO]\tResult from SGDT: limit[0x%04x]base[0x%08lx]\n", + gdt1.limit, gdt1.base); + + if (msw1 != msw2 || msw1 != msw3) + printf("[FAIL]\tAll the results of SMSW should be the same.\n"); + else + printf("[PASS]\tAll the results from SMSW are identical.\n"); + + if (memcmp(&gdt1, &gdt2, sizeof(gdt1))) + printf("[FAIL]\tAll the results of SGDT should be the same.\n"); + else + printf("[PASS]\tAll the results from SGDT are identical.\n"); + + if (memcmp(&idt1, &idt2, sizeof(idt1))) + printf("[FAIL]\tAll the results of SIDT should be the same.\n"); + else + printf("[PASS]\tAll the results from SIDT are identical.\n"); + + sethandler(SIGILL, sighandler, 0); + do_test(vm86, vmcode_umip_str - vmcode, VM86_SIGNAL, 0, + "STR instruction"); + clearhandler(SIGILL); + + sethandler(SIGILL, sighandler, 0); + do_test(vm86, vmcode_umip_sldt - vmcode, VM86_SIGNAL, 0, + "SLDT instruction"); + clearhandler(SIGILL); +} + int main(void) { struct vm86plus_struct v86; @@ -218,6 +302,9 @@ int main(void) v86.regs.eax = (unsigned int)-1; do_test(&v86, vmcode_int80 - vmcode, VM86_INTx, 0x80, "int80"); + /* UMIP -- should exit with INTx 0x80 unless UMIP was not disabled */ + do_umip_tests(&v86, addr); + /* Execute a null pointer */ v86.regs.cs = 0; v86.regs.ss = 0; diff --git a/tools/testing/selftests/x86/ldt_gdt.c b/tools/testing/selftests/x86/ldt_gdt.c index 961e3ee26c27..66e5ce5b91f0 100644 --- a/tools/testing/selftests/x86/ldt_gdt.c +++ b/tools/testing/selftests/x86/ldt_gdt.c @@ -115,7 +115,15 @@ static void check_valid_segment(uint16_t index, int ldt, return; } - if (ar != expected_ar) { + /* The SDM says "bits 19:16 are undefined". Thanks. */ + ar &= ~0xF0000; + + /* + * NB: Different Linux versions do different things with the + * accessed bit in set_thread_area(). + */ + if (ar != expected_ar && + (ldt || ar != (expected_ar | AR_ACCESSED))) { printf("[FAIL]\t%s entry %hu has AR 0x%08X but expected 0x%08X\n", (ldt ? "LDT" : "GDT"), index, ar, expected_ar); nerrs++; @@ -129,30 +137,51 @@ static void check_valid_segment(uint16_t index, int ldt, } } -static bool install_valid_mode(const struct user_desc *desc, uint32_t ar, - bool oldmode) +static bool install_valid_mode(const struct user_desc *d, uint32_t ar, + bool oldmode, bool ldt) { - int ret = syscall(SYS_modify_ldt, oldmode ? 1 : 0x11, - desc, sizeof(*desc)); - if (ret < -1) - errno = -ret; + struct user_desc desc = *d; + int ret; + + if (!ldt) { +#ifndef __i386__ + /* No point testing set_thread_area in a 64-bit build */ + return false; +#endif + if (!gdt_entry_num) + return false; + desc.entry_number = gdt_entry_num; + + ret = syscall(SYS_set_thread_area, &desc); + } else { + ret = syscall(SYS_modify_ldt, oldmode ? 1 : 0x11, + &desc, sizeof(desc)); + + if (ret < -1) + errno = -ret; + + if (ret != 0 && errno == ENOSYS) { + printf("[OK]\tmodify_ldt returned -ENOSYS\n"); + return false; + } + } + if (ret == 0) { - uint32_t limit = desc->limit; - if (desc->limit_in_pages) + uint32_t limit = desc.limit; + if (desc.limit_in_pages) limit = (limit << 12) + 4095; - check_valid_segment(desc->entry_number, 1, ar, limit, true); + check_valid_segment(desc.entry_number, ldt, ar, limit, true); return true; - } else if (errno == ENOSYS) { - printf("[OK]\tmodify_ldt returned -ENOSYS\n"); - return false; } else { - if (desc->seg_32bit) { - printf("[FAIL]\tUnexpected modify_ldt failure %d\n", + if (desc.seg_32bit) { + printf("[FAIL]\tUnexpected %s failure %d\n", + ldt ? "modify_ldt" : "set_thread_area", errno); nerrs++; return false; } else { - printf("[OK]\tmodify_ldt rejected 16 bit segment\n"); + printf("[OK]\t%s rejected 16 bit segment\n", + ldt ? "modify_ldt" : "set_thread_area"); return false; } } @@ -160,7 +189,15 @@ static bool install_valid_mode(const struct user_desc *desc, uint32_t ar, static bool install_valid(const struct user_desc *desc, uint32_t ar) { - return install_valid_mode(desc, ar, false); + bool ret = install_valid_mode(desc, ar, false, true); + + if (desc->contents <= 1 && desc->seg_32bit && + !desc->seg_not_present) { + /* Should work in the GDT, too. */ + install_valid_mode(desc, ar, false, false); + } + + return ret; } static void install_invalid(const struct user_desc *desc, bool oldmode) @@ -367,9 +404,24 @@ static void do_simple_tests(void) install_invalid(&desc, false); desc.seg_not_present = 0; - desc.read_exec_only = 0; desc.seg_32bit = 1; + desc.read_exec_only = 0; + desc.limit = 0xfffff; + install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA | AR_S | AR_P | AR_DB); + + desc.limit_in_pages = 1; + + install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA | AR_S | AR_P | AR_DB | AR_G); + desc.read_exec_only = 1; + install_valid(&desc, AR_DPL3 | AR_TYPE_RODATA | AR_S | AR_P | AR_DB | AR_G); + desc.contents = 1; + desc.read_exec_only = 0; + install_valid(&desc, AR_DPL3 | AR_TYPE_RWDATA_EXPDOWN | AR_S | AR_P | AR_DB | AR_G); + desc.read_exec_only = 1; + install_valid(&desc, AR_DPL3 | AR_TYPE_RODATA_EXPDOWN | AR_S | AR_P | AR_DB | AR_G); + + desc.limit = 0; install_invalid(&desc, true); } diff --git a/tools/testing/selftests/x86/protection_keys.c b/tools/testing/selftests/x86/protection_keys.c index 555e43ca846b..7a1cc0e56d2d 100644 --- a/tools/testing/selftests/x86/protection_keys.c +++ b/tools/testing/selftests/x86/protection_keys.c @@ -189,17 +189,29 @@ void lots_o_noops_around_write(int *write_to_me) #define u64 uint64_t #ifdef __i386__ -#define SYS_mprotect_key 380 -#define SYS_pkey_alloc 381 -#define SYS_pkey_free 382 + +#ifndef SYS_mprotect_key +# define SYS_mprotect_key 380 +#endif +#ifndef SYS_pkey_alloc +# define SYS_pkey_alloc 381 +# define SYS_pkey_free 382 +#endif #define REG_IP_IDX REG_EIP #define si_pkey_offset 0x14 + #else -#define SYS_mprotect_key 329 -#define SYS_pkey_alloc 330 -#define SYS_pkey_free 331 + +#ifndef SYS_mprotect_key +# define SYS_mprotect_key 329 +#endif +#ifndef SYS_pkey_alloc +# define SYS_pkey_alloc 330 +# define SYS_pkey_free 331 +#endif #define REG_IP_IDX REG_RIP #define si_pkey_offset 0x20 + #endif void dump_mem(void *dumpme, int len_bytes) |