[Fault-injection-developer] [RFC][PATCH] PCI configuration interceptor patch (kernel)
Status: Alpha
Brought to you by:
rustyl
From: Fumitake A. <ab...@jp...> - 2003-04-10 09:27:35
|
Dear All: Hello. My name is Fumitake ABE. I'm co-worker of Yasunori Goto. Please forgive him for not writing in a long time. We have implemented a pci configuration interceptor. We try to attach kprobe to wrappers for all pci configuration access functions. And more specifically, the address attached kprobe is as follows: [drivers/pci/access.c] 22 #define PCI_OP_READ(size,type,len) \ 23 int pci_bus_read_config_##size \ 24 (struct pci_bus *bus, unsigned int devfn, int pos, type *value) \ 25 { \ 26 int res; \ 27 unsigned long flags; \ 28 u32 data = 0; \ 29 if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ 30 spin_lock_irqsave(&pci_lock, flags); \ 31 res = bus->ops->read(bus, devfn, pos, len, &data); \ kprobe => 32 *value = (type)data; \ 33 spin_unlock_irqrestore(&pci_lock, flags); \ 34 return res; \ 35 } 36 37 #define PCI_OP_WRITE(size,type,len) \ 38 int pci_bus_write_config_##size \ 39 (struct pci_bus *bus, unsigned int devfn, int pos, type value) \ 40 { \ 41 int res; \ 42 unsigned long flags; \ 43 if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ 44 spin_lock_irqsave(&pci_lock, flags); \ kprobe => 45 res = bus->ops->write(bus, devfn, pos, len, value); \ 46 spin_unlock_irqrestore(&pci_lock, flags); \ 47 return res; \ 48 } I send another mail written how to use and attached utility patch. Please read also it. Regards, Fumitake ABE ab...@jp... === diff -urN linux-2.5.58-fith/arch/i386/Kconfig linux-2.5.58-fith-pci/arch/i386/Kconfig --- linux-2.5.58-fith/arch/i386/Kconfig 2003-04-07 10:27:21.000000000 +0900 +++ linux-2.5.58-fith-pci/arch/i386/Kconfig 2003-04-07 10:29:13.000000000 +0900 @@ -1656,6 +1656,12 @@ If in doubt, say N. +config FI_PCI + tristate "Fault injection PCI Interceptor (EXPERIMENTAL)" + depends on FI + help + This component adds the ability for Fault Injection to intercept + PCI Configuration register access. config FI_INTERNEL_TESTING bool "Fault Injection Internel Test Components (EXPERIMENTAL)" diff -urN linux-2.5.58-fith/drivers/fi/fi_core.c linux-2.5.58-fith-pci/drivers/fi/fi_core.c --- linux-2.5.58-fith/drivers/fi/fi_core.c 2003-04-07 10:27:17.000000000 +0900 +++ linux-2.5.58-fith-pci/drivers/fi/fi_core.c 2003-04-07 10:29:05.000000000 +0900 @@ -132,6 +132,7 @@ static struct trigger_attribute trigger_attr_operand; static struct trigger_attribute trigger_attr_count; static struct trigger_attribute trigger_attr_intcpt; +static struct trigger_attribute trigger_attr_pci; static inline void create_trigger_files(struct trigger *t) { @@ -147,6 +148,7 @@ sysfs_create_file(&t->kobj, &trigger_attr_operand.attr); sysfs_create_file(&t->kobj, &trigger_attr_count.attr); sysfs_create_file(&t->kobj, &trigger_attr_intcpt.attr); + sysfs_create_file(&t->kobj, &trigger_attr_pci.attr); } static inline void remove_trigger_files(struct trigger *t) @@ -163,6 +165,7 @@ sysfs_remove_file(&t->kobj, &trigger_attr_operand.attr); sysfs_remove_file(&t->kobj, &trigger_attr_count.attr); sysfs_remove_file(&t->kobj, &trigger_attr_intcpt.attr); + sysfs_remove_file(&t->kobj, &trigger_attr_pci.attr); } static inline void unarm_trigger(struct trigger *t) @@ -213,6 +216,10 @@ n->hertz = t->hertz; n->opcode = t->opcode; n->operand = t->operand; + n->pci.bus = t->pci.bus; + n->pci.slot = t->pci.slot; + n->pci.func = t->pci.func; + n->pci.reg_no = t->pci.reg_no; atomic_set(&n->count, 0); if (kobject_register(&n->kobj)) { @@ -524,6 +531,20 @@ .show = trigger_opcode_read, }; +static ssize_t trigger_pci_read(struct trigger * p, char * page) +{ + dbg("pci.bus = %#x, pci.slot = %#x, pci.func = %#x, pci.reg_no = %#x", + p->pci.bus, p->pci.slot, p->pci.func, p->pci.reg_no); + return sprintf(page, + "pci.bus = %#x, pci.slot = %#x, pci.func = %#x, pci.reg_no = %#x\n", + p->pci.bus, p->pci.slot, p->pci.func, p->pci.reg_no); +} + +static struct trigger_attribute trigger_attr_pci = { + .attr = { .name = "pci", .mode = S_IRUGO }, + .show = trigger_pci_read, +}; + static ssize_t trigger_operand_read(struct trigger * p, char * page) { dbg("operand = %i", p->operand); @@ -553,9 +574,9 @@ int ret = 0; struct trigger tmp; - /* 3 4 5 6 7 8 9 10 11 12 13 14 16 17 */ - /* ctl nm ic wa wb bm mn mx sk st pt hz oc oa */ - num = sscanf( page, "%15s %s %s %lx %x %x %i %i %i %i %x %i %i %x", + /* 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20*/ + /* ctl nm ic wa wb bm mn mx sk st pt hz bs sl fc rn oc oa*/ + num = sscanf( page, "%15s %s %s %lx %x %x %i %i %i %i %x %i %x %x %x %x %i %x", ctl, /* 3 */ tmp.kobj.name, /* 4 */ intcpt, /* 5 */ @@ -568,15 +589,20 @@ &tmp.stop, /* 12 */ &tmp.protection, /* 13 */ &tmp.hertz, /* 14 */ - (int *)&tmp.opcode, /* 15 */ - &tmp.operand); /* 16 */ + &tmp.pci.bus, /* 15 */ + &tmp.pci.slot, /* 16 */ + &tmp.pci.func, /* 17 */ + &tmp.pci.reg_no, /* 18 */ + (int *)&tmp.opcode, /* 19 */ + &tmp.operand); /* 20 */ + if (!num) { err("Invalid command format, translated no commands"); ret = -EINVAL; goto Done; } - if (!strcmp(ctl,"add") && num == 14) { + if (!strcmp(ctl,"add") && (num == 18)) { dbg("intcpt = '%s'", intcpt); ret = add_trigger(&tmp, intcpt); } else if (!strcmp(ctl,"del") && num == 2) { diff -urN linux-2.5.58-fith/drivers/fi/interceptors/Makefile linux-2.5.58-fith-pci/drivers/fi/interceptors/Makefile --- linux-2.5.58-fith/drivers/fi/interceptors/Makefile 2003-04-07 10:27:17.000000000 +0900 +++ linux-2.5.58-fith-pci/drivers/fi/interceptors/Makefile 2003-04-07 10:29:05.000000000 +0900 @@ -7,3 +7,4 @@ obj-$(CONFIG_FI_IRQ) += fi_irq.o obj-$(CONFIG_FI_DBP) += dbp/ obj-$(CONFIG_FI_PF) += pf/ +obj-$(CONFIG_FI_PCI) += pci/ diff -urN linux-2.5.58-fith/drivers/fi/interceptors/pci/Makefile linux-2.5.58-fith-pci/drivers/fi/interceptors/pci/Makefile --- linux-2.5.58-fith/drivers/fi/interceptors/pci/Makefile 1970-01-01 09:00:00.000000000 +0900 +++ linux-2.5.58-fith-pci/drivers/fi/interceptors/pci/Makefile 2003-04-07 10:29:05.000000000 +0900 @@ -0,0 +1,9 @@ +# +# Makefile for the linux kernel. +# +export-objs = fi_pci.o + +obj-$(CONFIG_FI_PCI) += fi_pci.o + + +EXTRA_AFLAGS := -traditional diff -urN linux-2.5.58-fith/drivers/fi/interceptors/pci/fi_pci.c linux-2.5.58-fith-pci/drivers/fi/interceptors/pci/fi_pci.c --- linux-2.5.58-fith/drivers/fi/interceptors/pci/fi_pci.c 1970-01-01 09:00:00.000000000 +0900 +++ linux-2.5.58-fith-pci/drivers/fi/interceptors/pci/fi_pci.c 2003-04-07 10:29:05.000000000 +0900 @@ -0,0 +1,387 @@ +/* + * fi_pci.c - pci configuration register access interceptor + * + * Copyright (C) FUJITSU LIMITED, 2003 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/config.h> +#include <linux/version.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <asm/processor.h> +#include <linux/pci.h> +#include <linux/list.h> +#include <linux/kprobes.h> + +#include <linux/fi.h> + +#define PP_READ 0 +#define PP_WRITE 1 + +/* write == 1 : read == 0 : others == -1 */ +#define PCIITC_WR_CHK(type) (type&0x10) ? 1 : (type&0x08) ? 0 : -1 + +struct corrupt { + unsigned long *preg; + unsigned long mask; +}; + +struct pci_conf_access { + struct list_head list; + struct trigger *trigger; + unsigned int bus; + unsigned int slot; + unsigned int func; + unsigned int reg_no; + unsigned int type; +}; + +struct inst_pci { + struct list_head list; + struct kprobe kpb; + int write; +}; + +static DECLARE_MUTEX(pci_sem); +static LIST_HEAD(pci_conf_acc_head); +static LIST_HEAD(inst_pci_head); +static struct interceptor pci_interceptor; + +/* + * Find inst_pci by addr + */ +static inline struct inst_pci * +find_inst_by_addr(void *addr) +{ + struct inst_pci *inst; + + list_for_each_entry(inst, &inst_pci_head, list) { + if(inst->kpb.addr == (kprobe_opcode_t *)addr) { + return inst; + } + } + return 0; +} + +/* + * Arm trigger + */ +static int +pciitc_arm(struct trigger *t) +{ + struct pci_conf_access *pca; + + if( !is_PCI(t->wp.type) ) { + err("Isn't PCI configuration register access"); + return -EINVAL; + } + if( t->pci.bus > 255 ) { + err("invalid bus number - %d", t->pci.bus); + return -EINVAL; + } + if( t->pci.slot > 31 ) { + err("invalid device number - %d", t->pci.slot); + return -EINVAL; + } + if( t->pci.func > 7 ) { + err("invalid function number - %d", t->pci.func); + return -EINVAL; + } + if( t->pci.reg_no > 255 ) { + err("invalid register address - %d", t->pci.reg_no); + return -EINVAL; + } + + list_for_each_entry( pca, &pci_conf_acc_head, list ) { + if( pca->bus == t->pci.bus && pca->slot == t->pci.slot && + pca->func == t->pci.func && pca->reg_no == t->pci.reg_no && + pca->type == t->wp.type) { + err("same attribute already exists"); + return -EINVAL; + } + } + + pca = (struct pci_conf_access *)kmalloc(sizeof(struct pci_conf_access), GFP_KERNEL); + if(!pca) { + err("Allocate memory failed"); + return -ENOMEM; + } + + pca->bus = t->pci.bus; + pca->slot = t->pci.slot; + pca->func = t->pci.func; + pca->reg_no = t->pci.reg_no; + + pca->type = t->wp.type; + + pca->trigger = t; + + list_add(&pca->list, &pci_conf_acc_head); + + return 0; +} + +/* + * Disarm trigger + */ +void +pciitc_disarm(struct trigger *t) +{ + struct pci_conf_access *pca; + + list_for_each_entry( pca, &pci_conf_acc_head, list ) { + if( pca->bus == t->pci.bus && pca->slot == t->pci.slot && + pca->func == t->pci.func && pca->reg_no == t->pci.reg_no && + pca->type == t->wp.type ) { + list_del(&pca->list); + kfree(pca); + } + } +} + +/* + * Corrupt - called by fi_execute_trigger + */ +void +pciitc_corrupt(__u32 dirty, void *data) +{ + unsigned long mask = ((struct corrupt *)data)->mask; + *(((struct corrupt *)data)->preg) &= ~mask; + *(((struct corrupt *)data)->preg) |= (dirty&mask); + dbg("corrupt data to:%#lX", dirty&mask); + return; +} + +/* + * Handler + */ +void +pciitc_pre_handler(struct kprobe *kprobe, struct pt_regs *regs) +{ + int len; + int bus; + int slot; + int func; + int reg_no; + unsigned int origin; + struct corrupt crpt; + struct pci_conf_access *pca; + struct inst_pci *inst; + struct pci_bus *pbus; + + pbus = *(struct pci_bus **)(®s->esp); + + bus = pbus->number; + slot = PCI_SLOT(*(unsigned int *)(®s->esp+1)); + func = PCI_FUNC(*(unsigned int *)(®s->esp+1)); + reg_no = *(int *)(®s->esp+2); + len = *(int *)(®s->esp+3); + + /* dbg("[%02x:%02x.%02x] %02x - %d", bus, slot, func, reg_no, len); */ + + inst = find_inst_by_addr(kprobe->addr); + if(!inst) { + goto exit; + } + + list_for_each_entry( pca, &pci_conf_acc_head, list ) { + if( pca->bus == bus && pca->slot == slot && + pca->func == func && pca->reg_no == reg_no && + (PCIITC_WR_CHK(pca->type)) == inst->write ) { + break; + } + } + + if(&pca->list == &pci_conf_acc_head) goto exit; + + switch(len) { + case 1: + crpt.mask = 0xFF; + break; + case 2: + crpt.mask = 0xFFFF; + break; + case 4: + crpt.mask = 0xFFFFFFFF; + break; + default: + err("Wrong length - %d", len); + goto exit; + } + + crpt.preg = *(unsigned long **)(®s->esp+4); + origin = **(unsigned int **)(®s->esp+4) & crpt.mask; + + fi_execute_trigger( pca->trigger, &pci_interceptor, origin, len, inst->write, &crpt ); + + exit: + return; +} + +/* + * set kprobes + */ +int +pciitc_insert(const char *page) +{ + char tmp[16]; + int num=0; + int write=-1; + unsigned long addr=0; + struct inst_pci *inst; + + num = sscanf(page, "%15s %i %lx", tmp, &write, &addr); + if (num != 3) { + err("Invalid command format, translated no commands"); + return -EINVAL; + } + if (write!=PP_WRITE && write!=PP_READ) { + err("Invalid command format, not evaluate write/read"); + return -EINVAL; + } + + inst = (struct inst_pci *)kmalloc(sizeof(struct inst_pci *), GFP_KERNEL); + if(!inst) { + err("Allocate memory failed"); + return -ENOMEM; + } + + inst->kpb.addr = (kprobe_opcode_t *)addr; + inst->kpb.pre_handler = pciitc_pre_handler; + inst->kpb.post_handler = NULL; + inst->kpb.fault_handler = NULL; + + if( register_kprobe(&inst->kpb) < 0 ) { + kfree(inst); + err("register_kprobe failed"); + return -EINVAL; + } + + inst->write = write; + + list_add(&inst->list, &inst_pci_head); + return 0; +} + +/* + * unset kprobes + */ +void +pciitc_delete(void) +{ + struct inst_pci *inst; + + while(!list_empty(&inst_pci_head)) { + inst = list_entry(inst_pci_head.next, struct inst_pci, list); + list_del(&inst->list); + unregister_kprobe(&inst->kpb); + kfree(inst); + } +} + +/* + * show : cat /sys/fi/interceptor/pci/ctl + */ +static ssize_t +ctl_show(struct interceptor *p, char *page) +{ + return 0; +} + +/* + * store : echo "..." > /sys/fi/interceptor/pci/ctl + */ +static ssize_t +ctl_store(struct interceptor *p, const char *page, size_t count) +{ + char ctl[16]; + int num; + int error = 0; + + num = sscanf(page, "%15s", ctl); + + if(!num) { + err("Invalid command format, translated no commands"); + error = -EINVAL; + return error; + } + + down(&pci_sem); + if(!strncmp(ctl, "add", 3)) { + error = pciitc_insert(page); + } else if(!strncmp(ctl, "clear", 5)) { + pciitc_delete(); + } else { + err("Unknown command %s", page); + error = -EINVAL; + } + up(&pci_sem); + + return error ? error : count; +} + +static struct interceptor_attribute attr_ctl = { + .attr = { .name = "ctl", .mode = 0644 }, + .show = ctl_show, + .store = ctl_store +}; + +static struct interceptor pci_interceptor = { + .kobj = { .name = "pci" }, + .arm = pciitc_arm, + .disarm = pciitc_disarm, + .corrupt = pciitc_corrupt +}; + +/* + * module init + */ +static int __init +pciitc_start(void) +{ + if(fi_register_interceptor(&pci_interceptor)) { + dbg("Failed to register DBP Interceptor"); + return -EINVAL; + } + + sysfs_create_file(&pci_interceptor.kobj, &attr_ctl.attr); + + EXPORT_NO_SYMBOLS; + + return 0; +} + +/* + * module exit + */ +static void __exit +pciitc_stop(void) +{ + pciitc_delete(); + sysfs_remove_file(&pci_interceptor.kobj, &attr_ctl.attr); + fi_unregister_interceptor(&pci_interceptor); + return; +} + +module_init(pciitc_start); +module_exit(pciitc_stop); + +MODULE_AUTHOR("Yasunori GOTO"); +MODULE_DESCRIPTION("PCI conf interceptor for FI."); +MODULE_LICENSE("GPL"); diff -urN linux-2.5.58-fith/include/linux/fi.h linux-2.5.58-fith-pci/include/linux/fi.h --- linux-2.5.58-fith/include/linux/fi.h 2003-04-07 10:27:19.000000000 +0900 +++ linux-2.5.58-fith-pci/include/linux/fi.h 2003-04-07 10:29:08.000000000 +0900 @@ -11,6 +11,7 @@ #define FI_WPTYPE_READ 8 #define FI_WPTYPE_WRITE 16 #define FI_WPTYPE_EXEC 32 +#define FI_WPTYPE_PCI 64 #define FI_WPTYPE_LEN_MASK 3 #define FI_WPTYPE_ALL_MASK ( FI_WPTYPE_MMIO \ @@ -34,6 +35,16 @@ unsigned int type; }; +/** + * define pci configuration register access structure. + */ +struct pci_conf_reg { + int bus; + int slot; + int func; + int reg_no; +}; + #define WP_LEN(wp) (1<<((unsigned long)(wp.type)&3)) typedef enum { @@ -76,6 +87,10 @@ struct list_head cs_list; struct interceptor *intcpt; atomic_t count; + + /* pci configuration register */ + struct pci_conf_reg pci; + /* opaque data struct for register */ void *data; struct kobject kobj; @@ -152,6 +167,7 @@ inline static int is_read(int type) { return (type&0x08)?1:0; }; inline static int is_write(int type) { return (type&0x10)?1:0; }; inline static int is_execute(int type) { return (type&0x20)?1:0; }; +inline static int is_PCI(int type) { return (type&0x40)?1:0; }; inline static int access_len(int type) { return 1UL<<(type&0x03);}; #endif /* _FAULT_INJECTION_H */ |