diff options
Diffstat (limited to 'arch/x86/mm/kmmio.c')
-rw-r--r-- | arch/x86/mm/kmmio.c | 58 |
1 files changed, 33 insertions, 25 deletions
diff --git a/arch/x86/mm/kmmio.c b/arch/x86/mm/kmmio.c index 16ccbd77917f..5d0e67fff1a6 100644 --- a/arch/x86/mm/kmmio.c +++ b/arch/x86/mm/kmmio.c | |||
@@ -5,6 +5,8 @@ | |||
5 | * 2008 Pekka Paalanen <pq@iki.fi> | 5 | * 2008 Pekka Paalanen <pq@iki.fi> |
6 | */ | 6 | */ |
7 | 7 | ||
8 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
9 | |||
8 | #include <linux/list.h> | 10 | #include <linux/list.h> |
9 | #include <linux/rculist.h> | 11 | #include <linux/rculist.h> |
10 | #include <linux/spinlock.h> | 12 | #include <linux/spinlock.h> |
@@ -19,6 +21,7 @@ | |||
19 | #include <linux/kdebug.h> | 21 | #include <linux/kdebug.h> |
20 | #include <linux/mutex.h> | 22 | #include <linux/mutex.h> |
21 | #include <linux/io.h> | 23 | #include <linux/io.h> |
24 | #include <linux/slab.h> | ||
22 | #include <asm/cacheflush.h> | 25 | #include <asm/cacheflush.h> |
23 | #include <asm/tlbflush.h> | 26 | #include <asm/tlbflush.h> |
24 | #include <linux/errno.h> | 27 | #include <linux/errno.h> |
@@ -136,7 +139,7 @@ static int clear_page_presence(struct kmmio_fault_page *f, bool clear) | |||
136 | pte_t *pte = lookup_address(f->page, &level); | 139 | pte_t *pte = lookup_address(f->page, &level); |
137 | 140 | ||
138 | if (!pte) { | 141 | if (!pte) { |
139 | pr_err("kmmio: no pte for page 0x%08lx\n", f->page); | 142 | pr_err("no pte for page 0x%08lx\n", f->page); |
140 | return -1; | 143 | return -1; |
141 | } | 144 | } |
142 | 145 | ||
@@ -148,7 +151,7 @@ static int clear_page_presence(struct kmmio_fault_page *f, bool clear) | |||
148 | clear_pte_presence(pte, clear, &f->old_presence); | 151 | clear_pte_presence(pte, clear, &f->old_presence); |
149 | break; | 152 | break; |
150 | default: | 153 | default: |
151 | pr_err("kmmio: unexpected page level 0x%x.\n", level); | 154 | pr_err("unexpected page level 0x%x.\n", level); |
152 | return -1; | 155 | return -1; |
153 | } | 156 | } |
154 | 157 | ||
@@ -170,13 +173,14 @@ static int clear_page_presence(struct kmmio_fault_page *f, bool clear) | |||
170 | static int arm_kmmio_fault_page(struct kmmio_fault_page *f) | 173 | static int arm_kmmio_fault_page(struct kmmio_fault_page *f) |
171 | { | 174 | { |
172 | int ret; | 175 | int ret; |
173 | WARN_ONCE(f->armed, KERN_ERR "kmmio page already armed.\n"); | 176 | WARN_ONCE(f->armed, KERN_ERR pr_fmt("kmmio page already armed.\n")); |
174 | if (f->armed) { | 177 | if (f->armed) { |
175 | pr_warning("kmmio double-arm: page 0x%08lx, ref %d, old %d\n", | 178 | pr_warning("double-arm: page 0x%08lx, ref %d, old %d\n", |
176 | f->page, f->count, !!f->old_presence); | 179 | f->page, f->count, !!f->old_presence); |
177 | } | 180 | } |
178 | ret = clear_page_presence(f, true); | 181 | ret = clear_page_presence(f, true); |
179 | WARN_ONCE(ret < 0, KERN_ERR "kmmio arming 0x%08lx failed.\n", f->page); | 182 | WARN_ONCE(ret < 0, KERN_ERR pr_fmt("arming 0x%08lx failed.\n"), |
183 | f->page); | ||
180 | f->armed = true; | 184 | f->armed = true; |
181 | return ret; | 185 | return ret; |
182 | } | 186 | } |
@@ -203,7 +207,7 @@ static void disarm_kmmio_fault_page(struct kmmio_fault_page *f) | |||
203 | */ | 207 | */ |
204 | /* | 208 | /* |
205 | * Interrupts are disabled on entry as trap3 is an interrupt gate | 209 | * Interrupts are disabled on entry as trap3 is an interrupt gate |
206 | * and they remain disabled thorough out this function. | 210 | * and they remain disabled throughout this function. |
207 | */ | 211 | */ |
208 | int kmmio_handler(struct pt_regs *regs, unsigned long addr) | 212 | int kmmio_handler(struct pt_regs *regs, unsigned long addr) |
209 | { | 213 | { |
@@ -240,24 +244,21 @@ int kmmio_handler(struct pt_regs *regs, unsigned long addr) | |||
240 | * condition needs handling by do_page_fault(), the | 244 | * condition needs handling by do_page_fault(), the |
241 | * page really not being present is the most common. | 245 | * page really not being present is the most common. |
242 | */ | 246 | */ |
243 | pr_debug("kmmio: secondary hit for 0x%08lx CPU %d.\n", | 247 | pr_debug("secondary hit for 0x%08lx CPU %d.\n", |
244 | addr, smp_processor_id()); | 248 | addr, smp_processor_id()); |
245 | 249 | ||
246 | if (!faultpage->old_presence) | 250 | if (!faultpage->old_presence) |
247 | pr_info("kmmio: unexpected secondary hit for " | 251 | pr_info("unexpected secondary hit for address 0x%08lx on CPU %d.\n", |
248 | "address 0x%08lx on CPU %d.\n", addr, | 252 | addr, smp_processor_id()); |
249 | smp_processor_id()); | ||
250 | } else { | 253 | } else { |
251 | /* | 254 | /* |
252 | * Prevent overwriting already in-flight context. | 255 | * Prevent overwriting already in-flight context. |
253 | * This should not happen, let's hope disarming at | 256 | * This should not happen, let's hope disarming at |
254 | * least prevents a panic. | 257 | * least prevents a panic. |
255 | */ | 258 | */ |
256 | pr_emerg("kmmio: recursive probe hit on CPU %d, " | 259 | pr_emerg("recursive probe hit on CPU %d, for address 0x%08lx. Ignoring.\n", |
257 | "for address 0x%08lx. Ignoring.\n", | 260 | smp_processor_id(), addr); |
258 | smp_processor_id(), addr); | 261 | pr_emerg("previous hit was at 0x%08lx.\n", ctx->addr); |
259 | pr_emerg("kmmio: previous hit was at 0x%08lx.\n", | ||
260 | ctx->addr); | ||
261 | disarm_kmmio_fault_page(faultpage); | 262 | disarm_kmmio_fault_page(faultpage); |
262 | } | 263 | } |
263 | goto no_kmmio_ctx; | 264 | goto no_kmmio_ctx; |
@@ -302,7 +303,7 @@ no_kmmio: | |||
302 | 303 | ||
303 | /* | 304 | /* |
304 | * Interrupts are disabled on entry as trap1 is an interrupt gate | 305 | * Interrupts are disabled on entry as trap1 is an interrupt gate |
305 | * and they remain disabled thorough out this function. | 306 | * and they remain disabled throughout this function. |
306 | * This must always get called as the pair to kmmio_handler(). | 307 | * This must always get called as the pair to kmmio_handler(). |
307 | */ | 308 | */ |
308 | static int post_kmmio_handler(unsigned long condition, struct pt_regs *regs) | 309 | static int post_kmmio_handler(unsigned long condition, struct pt_regs *regs) |
@@ -316,8 +317,8 @@ static int post_kmmio_handler(unsigned long condition, struct pt_regs *regs) | |||
316 | * something external causing them (f.e. using a debugger while | 317 | * something external causing them (f.e. using a debugger while |
317 | * mmio tracing enabled), or erroneous behaviour | 318 | * mmio tracing enabled), or erroneous behaviour |
318 | */ | 319 | */ |
319 | pr_warning("kmmio: unexpected debug trap on CPU %d.\n", | 320 | pr_warning("unexpected debug trap on CPU %d.\n", |
320 | smp_processor_id()); | 321 | smp_processor_id()); |
321 | goto out; | 322 | goto out; |
322 | } | 323 | } |
323 | 324 | ||
@@ -425,7 +426,7 @@ int register_kmmio_probe(struct kmmio_probe *p) | |||
425 | list_add_rcu(&p->list, &kmmio_probes); | 426 | list_add_rcu(&p->list, &kmmio_probes); |
426 | while (size < size_lim) { | 427 | while (size < size_lim) { |
427 | if (add_kmmio_fault_page(p->addr + size)) | 428 | if (add_kmmio_fault_page(p->addr + size)) |
428 | pr_err("kmmio: Unable to set page fault.\n"); | 429 | pr_err("Unable to set page fault.\n"); |
429 | size += PAGE_SIZE; | 430 | size += PAGE_SIZE; |
430 | } | 431 | } |
431 | out: | 432 | out: |
@@ -490,7 +491,7 @@ static void remove_kmmio_fault_pages(struct rcu_head *head) | |||
490 | * 2. remove_kmmio_fault_pages() | 491 | * 2. remove_kmmio_fault_pages() |
491 | * Remove the pages from kmmio_page_table. | 492 | * Remove the pages from kmmio_page_table. |
492 | * 3. rcu_free_kmmio_fault_pages() | 493 | * 3. rcu_free_kmmio_fault_pages() |
493 | * Actally free the kmmio_fault_page structs as with RCU. | 494 | * Actually free the kmmio_fault_page structs as with RCU. |
494 | */ | 495 | */ |
495 | void unregister_kmmio_probe(struct kmmio_probe *p) | 496 | void unregister_kmmio_probe(struct kmmio_probe *p) |
496 | { | 497 | { |
@@ -511,7 +512,7 @@ void unregister_kmmio_probe(struct kmmio_probe *p) | |||
511 | 512 | ||
512 | drelease = kmalloc(sizeof(*drelease), GFP_ATOMIC); | 513 | drelease = kmalloc(sizeof(*drelease), GFP_ATOMIC); |
513 | if (!drelease) { | 514 | if (!drelease) { |
514 | pr_crit("kmmio: leaking kmmio_fault_page objects.\n"); | 515 | pr_crit("leaking kmmio_fault_page objects.\n"); |
515 | return; | 516 | return; |
516 | } | 517 | } |
517 | drelease->release_list = release_list; | 518 | drelease->release_list = release_list; |
@@ -538,10 +539,17 @@ static int | |||
538 | kmmio_die_notifier(struct notifier_block *nb, unsigned long val, void *args) | 539 | kmmio_die_notifier(struct notifier_block *nb, unsigned long val, void *args) |
539 | { | 540 | { |
540 | struct die_args *arg = args; | 541 | struct die_args *arg = args; |
542 | unsigned long* dr6_p = (unsigned long *)ERR_PTR(arg->err); | ||
541 | 543 | ||
542 | if (val == DIE_DEBUG && (arg->err & DR_STEP)) | 544 | if (val == DIE_DEBUG && (*dr6_p & DR_STEP)) |
543 | if (post_kmmio_handler(arg->err, arg->regs) == 1) | 545 | if (post_kmmio_handler(*dr6_p, arg->regs) == 1) { |
546 | /* | ||
547 | * Reset the BS bit in dr6 (pointed by args->err) to | ||
548 | * denote completion of processing | ||
549 | */ | ||
550 | *dr6_p &= ~DR_STEP; | ||
544 | return NOTIFY_STOP; | 551 | return NOTIFY_STOP; |
552 | } | ||
545 | 553 | ||
546 | return NOTIFY_DONE; | 554 | return NOTIFY_DONE; |
547 | } | 555 | } |