1/*
2 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7#include <config.h>
8
9#ifdef CONFIG_HARDWARE_DEBUG_API
10
11#include <arch/machine/debug.h>
12#include <mode/machine/debug.h>
13#include <arch/machine.h>
14#include <machine/registerset.h>
15#include <sel4/plat/api/constants.h> /* seL4_NumHWBReakpoints */
16
17/* Intel manual Vol3, 17.2.4 */
18#define X86_DEBUG_BP_SIZE_1B                (0x0u)
19#define X86_DEBUG_BP_SIZE_2B                (0x1u)
20#define X86_DEBUG_BP_SIZE_4B                (0x3u)
21#define X86_DEBUG_BP_SIZE_8B                (0x2u)
22
23#define X86_DEBUG_BP0_SIZE_SHIFT            (18)
24#define X86_DEBUG_BP1_SIZE_SHIFT            (22)
25#define X86_DEBUG_BP2_SIZE_SHIFT            (26)
26#define X86_DEBUG_BP3_SIZE_SHIFT            (30)
27
28/* NOTE: Intel manual 17.2.4:
29 * I/O breakpoints are supported by every processor later than i486, but only
30 * when CR4.DE=1.
31 * When CR4.DE=0, or if processor is earlier than i586, this bit is "Undefined",
32 * which is not the same as "Reserved", so it won't trigger an exception - it
33 * will just cause an undefined reaction from the CPU.
34 */
35#define X86_DEBUG_BP_TYPE_IO                (0x2u)
36#define X86_DEBUG_BP_TYPE_INSTR             (0x0u)
37#define X86_DEBUG_BP_TYPE_DATA_WRITE        (0x1u)
38#define X86_DEBUG_BP_TYPE_DATA_READWRITE    (0x3u)
39
40#define X86_DEBUG_BP0_TYPE_SHIFT            (16)
41#define X86_DEBUG_BP1_TYPE_SHIFT            (20)
42#define X86_DEBUG_BP2_TYPE_SHIFT            (24)
43#define X86_DEBUG_BP3_TYPE_SHIFT            (28)
44
45#define X86_DEBUG_EFLAGS_TRAP_FLAG    ((word_t)BIT(8))
46#define X86_DEBUG_EFLAGS_RESUME_FLAG    ((word_t)BIT(16))
47#define X86_DEBUG_DR6_SINGLE_STEP_FLAG  ((word_t)BIT(14))
48
49#define X86_DEBUG_DR6_BP_MASK     (0xFu)
50
51static bool_t byte8_bps_supported = false;
52
53bool_t byte8BreakpointsSupported(void)
54{
55    return byte8_bps_supported;
56}
57
58static inline void bitwiseAndDr6Reg(word_t mask)
59{
60    word_t tmp;
61
62    tmp = readDr6Reg() & mask;
63    writeDr6Reg(tmp);
64}
65
66static inline word_t readDr7Context(tcb_t *t)
67{
68    return t->tcbArch.tcbContext.breakpointState.dr[5];
69}
70
71static inline void bitwiseOrDr7Context(tcb_t *t, word_t val)
72{
73    t->tcbArch.tcbContext.breakpointState.dr[5] |= val;
74}
75
76static inline void bitwiseAndDr7Context(tcb_t *t, word_t mask)
77{
78    t->tcbArch.tcbContext.breakpointState.dr[5] &= mask;
79}
80
81static void unsetDr7BitsFor(tcb_t *t, uint16_t bp_num)
82{
83    word_t mask;
84
85    switch (bp_num) {
86    case 0:
87        mask = (0x3u << X86_DEBUG_BP0_SIZE_SHIFT) | (0x3u << X86_DEBUG_BP0_TYPE_SHIFT);
88        break;
89    case 1:
90        mask = (0x3u << X86_DEBUG_BP1_SIZE_SHIFT) | (0x3u << X86_DEBUG_BP1_TYPE_SHIFT);
91        break;
92    case 2:
93        mask = (0x3u << X86_DEBUG_BP2_SIZE_SHIFT) | (0x3u << X86_DEBUG_BP2_TYPE_SHIFT);
94        break;
95    default: /* 3 */
96        assert(bp_num == 3);
97        mask = (0x3u << X86_DEBUG_BP3_SIZE_SHIFT) | (0x3u << X86_DEBUG_BP3_TYPE_SHIFT);
98        break;
99    }
100
101    mask = ~mask;
102    bitwiseAndDr7Context(t, mask);
103}
104
105/** Converts an seL4_BreakpointType value into the underlying hardware
106 * equivalent.
107 * @param bp_num Breakpoint number.
108 * @param type One of the values of seL4_BreakpointType.
109 * @param rw Access trigger condition (read/write).
110 * @return Hardware specific register value representing the inputs.
111 */
112PURE static inline word_t convertTypeAndAccessToArch(uint16_t bp_num, word_t type, word_t rw)
113{
114    switch (type) {
115    case seL4_InstructionBreakpoint:
116        type = X86_DEBUG_BP_TYPE_INSTR;
117        break;
118    default: /* seL4_DataBreakpoint */
119        assert(type == seL4_DataBreakpoint);
120        type = (rw == seL4_BreakOnWrite)
121               ? X86_DEBUG_BP_TYPE_DATA_WRITE
122               : X86_DEBUG_BP_TYPE_DATA_READWRITE;
123    }
124
125    switch (bp_num) {
126    case 0:
127        return type << X86_DEBUG_BP0_TYPE_SHIFT;
128    case 1:
129        return type << X86_DEBUG_BP1_TYPE_SHIFT;
130    case 2:
131        return type << X86_DEBUG_BP2_TYPE_SHIFT;
132    default: /* 3 */
133        assert(bp_num == 3);
134        return type << X86_DEBUG_BP3_TYPE_SHIFT;
135    }
136}
137
138/** Reverse of convertTypeAndAccessToArch(): converts hardware values into
139 * seL4 API values.
140 * @param dr7 Hardware register value as input for conversion.
141 * @param bp_num Breakpoint number.
142 * @param type[out] Converted type value.
143 * @param rw[out] Converted output access trigger value.
144 */
145typedef struct {
146    word_t type, rw;
147} convertedTypeAndAccess_t;
148
149PURE static inline convertedTypeAndAccess_t convertArchToTypeAndAccess(word_t dr7, uint16_t bp_num)
150{
151    convertedTypeAndAccess_t ret;
152
153    switch (bp_num) {
154    case 0:
155        dr7 &= 0x3u << X86_DEBUG_BP0_TYPE_SHIFT;
156        dr7 >>= X86_DEBUG_BP0_TYPE_SHIFT;
157        break;
158    case 1:
159        dr7 &= 0x3u << X86_DEBUG_BP1_TYPE_SHIFT;
160        dr7 >>= X86_DEBUG_BP1_TYPE_SHIFT;
161        break;
162    case 2:
163        dr7 &= 0x3u << X86_DEBUG_BP2_TYPE_SHIFT;
164        dr7 >>= X86_DEBUG_BP2_TYPE_SHIFT;
165        break;
166    default: /* 3 */
167        assert(bp_num == 3);
168        dr7 &= 0x3u << X86_DEBUG_BP3_TYPE_SHIFT;
169        dr7 >>= X86_DEBUG_BP3_TYPE_SHIFT;
170    }
171
172    switch (dr7) {
173    case X86_DEBUG_BP_TYPE_INSTR:
174        ret.type = seL4_InstructionBreakpoint;
175        ret.rw = seL4_BreakOnRead;
176        break;
177    case X86_DEBUG_BP_TYPE_DATA_WRITE:
178        ret.type = seL4_DataBreakpoint;
179        ret.rw = seL4_BreakOnWrite;
180        break;
181    default: /* Read-write */
182        assert(dr7 == X86_DEBUG_BP_TYPE_DATA_READWRITE);
183        ret.type = seL4_DataBreakpoint;
184        ret.rw = seL4_BreakOnReadWrite;
185        break;
186    }
187    return ret;
188}
189
190/** Converts an integer size number into an equivalent hardware register value.
191 * @param n Breakpoint number.
192 * @param type One value from seL4_BreakpointType.
193 * @param size An integer for the operand size of the breakpoint.
194 * @return Converted, hardware-specific value.
195 */
196PURE static inline word_t convertSizeToArch(uint16_t bp_num, word_t type, word_t size)
197{
198    if (type == seL4_InstructionBreakpoint) {
199        /* Intel manual vol3 17.2.4:
200         * "If the corresponding RWn field in register DR7 is 00 (instruction
201         * execution), then the LENn field should also be 00"
202         */
203        size = 0;
204    } else {
205        switch (size) {
206        case 1:
207            size = X86_DEBUG_BP_SIZE_1B;
208            break;
209        case 2:
210            size = X86_DEBUG_BP_SIZE_2B;
211            break;
212        case 8:
213            size = X86_DEBUG_BP_SIZE_8B;
214            break;
215        default: /* 4B */
216            assert(size == 4);
217            size = X86_DEBUG_BP_SIZE_4B;
218        }
219    }
220
221    switch (bp_num) {
222    case 0:
223        return size << X86_DEBUG_BP0_SIZE_SHIFT;
224    case 1:
225        return size << X86_DEBUG_BP1_SIZE_SHIFT;
226    case 2:
227        return size << X86_DEBUG_BP2_SIZE_SHIFT;
228    default: /* 3 */
229        assert(bp_num == 3);
230        return size << X86_DEBUG_BP3_SIZE_SHIFT;
231    }
232}
233
234/** Reverse of convertSizeToArch(): converts a hardware-specific size value
235 * into an integer representation.
236 * @param dr7 Hardware register value as input.
237 * @param n Breakpoint number.
238 * @return Converted size value.
239 */
240PURE static inline word_t convertArchToSize(word_t dr7, uint16_t bp_num)
241{
242    word_t type;
243
244    switch (bp_num) {
245    case 0:
246        type = dr7 & (0x3u << X86_DEBUG_BP0_TYPE_SHIFT);
247        type >>= X86_DEBUG_BP0_TYPE_SHIFT;
248        dr7 &= 0x3u << X86_DEBUG_BP0_SIZE_SHIFT;
249        dr7 >>= X86_DEBUG_BP0_SIZE_SHIFT;
250        break;
251    case 1:
252        type = dr7 & (0x3u << X86_DEBUG_BP1_TYPE_SHIFT);
253        type >>= X86_DEBUG_BP1_TYPE_SHIFT;
254        dr7 &= 0x3u << X86_DEBUG_BP1_SIZE_SHIFT;
255        dr7 >>= X86_DEBUG_BP1_SIZE_SHIFT;
256        break;
257    case 2:
258        type = dr7 & (0x3u << X86_DEBUG_BP2_TYPE_SHIFT);
259        type >>= X86_DEBUG_BP2_TYPE_SHIFT;
260        dr7 &= 0x3u << X86_DEBUG_BP2_SIZE_SHIFT;
261        dr7 >>= X86_DEBUG_BP2_SIZE_SHIFT;
262        break;
263    default: /* 3 */
264        assert(bp_num == 3);
265        type = dr7 & (0x3u << X86_DEBUG_BP3_TYPE_SHIFT);
266        type >>= X86_DEBUG_BP3_TYPE_SHIFT;
267        dr7 &= 0x3u << X86_DEBUG_BP3_SIZE_SHIFT;
268        dr7 >>= X86_DEBUG_BP3_SIZE_SHIFT;
269    }
270
271    /* Force size to 0 if type is instruction breakpoint. */
272    if (type == X86_DEBUG_BP_TYPE_INSTR) {
273        return 0;
274    }
275
276    switch (dr7) {
277    case X86_DEBUG_BP_SIZE_1B:
278        return 1;
279    case X86_DEBUG_BP_SIZE_2B:
280        return 2;
281    case X86_DEBUG_BP_SIZE_8B:
282        return 8;
283    default: /* 4B */
284        assert(dr7 == X86_DEBUG_BP_SIZE_4B);
285        return 4;
286    }
287}
288
289/** Enables a breakpoint.
290 * @param bp_num Hardware breakpoint ID. Usually an integer from 0..N.
291 */
292static void enableBreakpoint(tcb_t *t, uint16_t bp_num)
293{
294    word_t enable_bit;
295
296    assert(t != NULL);
297    assert(bp_num < X86_DEBUG_BP_N_REGS);
298
299    switch (bp_num) {
300    case 0:
301        enable_bit = X86_DEBUG_BP0_ENABLE_BIT;
302        break;
303    case 1:
304        enable_bit = X86_DEBUG_BP1_ENABLE_BIT;
305        break;
306    case 2:
307        enable_bit = X86_DEBUG_BP2_ENABLE_BIT;
308        break;
309    default:
310        enable_bit = X86_DEBUG_BP3_ENABLE_BIT;
311        break;
312    }
313
314    bitwiseOrDr7Context(t, enable_bit);
315}
316
317/** Disables a breakpoint without clearing its configuration.
318 * @param bp_num Hardware breakpoint ID. Usually an integer from 0..N.
319 */
320static void disableBreakpoint(tcb_t *t, uint16_t bp_num)
321{
322    word_t disable_mask;
323
324    assert(t != NULL);
325    assert(bp_num < X86_DEBUG_BP_N_REGS);
326
327    switch (bp_num) {
328    case 0:
329        disable_mask = ~X86_DEBUG_BP0_ENABLE_BIT;
330        break;
331    case 1:
332        disable_mask = ~X86_DEBUG_BP1_ENABLE_BIT;
333        break;
334    case 2:
335        disable_mask = ~X86_DEBUG_BP2_ENABLE_BIT;
336        break;
337    default:
338        disable_mask = ~X86_DEBUG_BP3_ENABLE_BIT;
339        break;
340    }
341
342    bitwiseAndDr7Context(t, disable_mask);
343}
344
345/** Returns a boolean for whether or not a breakpoint is enabled.
346 * @param bp_num Hardware breakpoint ID. Usually an integer from 0..N.
347 */
348static bool_t breakpointIsEnabled(tcb_t *t, uint16_t bp_num)
349{
350    word_t dr7;
351
352    assert(t != NULL);
353    assert(bp_num < X86_DEBUG_BP_N_REGS);
354
355    dr7 = readDr7Context(t);
356    switch (bp_num) {
357    case 0:
358        return !!(dr7 & X86_DEBUG_BP0_ENABLE_BIT);
359    case 1:
360        return !!(dr7 & X86_DEBUG_BP1_ENABLE_BIT);
361    case 2:
362        return !!(dr7 & X86_DEBUG_BP2_ENABLE_BIT);
363    default:
364        return !!(dr7 & X86_DEBUG_BP3_ENABLE_BIT);
365    }
366}
367
368static void setBpVaddrContext(tcb_t *t, uint16_t bp_num, word_t vaddr)
369{
370    assert(t != NULL);
371    user_breakpoint_state_t *ubs = &t->tcbArch.tcbContext.breakpointState;
372
373    switch (bp_num) {
374    case 0:
375        ubs->dr[0] = vaddr;
376        break;
377    case 1:
378        ubs->dr[1] = vaddr;
379        break;
380    case 2:
381        ubs->dr[2] = vaddr;
382        break;
383    default:
384        assert(bp_num == 3);
385        ubs->dr[3] = vaddr;
386        break;
387    }
388    return;
389}
390
391/** Backend for the seL4_TCB_SetBreakpoint invocation.
392 *
393 * @param uds Arch TCB register context structure.
394 * @param bp_num Hardware breakpoint ID.
395 * @param vaddr USerspace virtual address on which you'd like this breakpoing
396 *        to trigger.
397 * @param types One of the seL4_BreakpointType values.
398 * @param size positive integer indicating the byte-range size that should
399 *        trigger the breakpoint. 0 is valid for Instruction breakpoints.
400 * @param rw Access type that should trigger the BP (read/write).
401 */
402void setBreakpoint(tcb_t *t,
403                   uint16_t bp_num, word_t vaddr, word_t types, word_t size, word_t rw)
404{
405    word_t dr7val;
406
407    assert(t != NULL);
408
409    dr7val = convertTypeAndAccessToArch(bp_num, types, rw);
410    dr7val |= convertSizeToArch(bp_num, types, size);
411
412    setBpVaddrContext(t, bp_num, vaddr);
413    unsetDr7BitsFor(t, bp_num);
414    bitwiseOrDr7Context(t, dr7val);
415    enableBreakpoint(t, bp_num);
416}
417
418static word_t getBpVaddrContext(tcb_t *t, uint16_t bp_num)
419{
420    assert(t != NULL);
421    user_breakpoint_state_t *ubs = &t->tcbArch.tcbContext.breakpointState;
422
423    switch (bp_num) {
424    case 0:
425        return ubs->dr[0];
426    case 1:
427        return ubs->dr[1];
428    case 2:
429        return ubs->dr[2];
430    default:
431        assert(bp_num == 3);
432        return ubs->dr[3];
433    }
434}
435
436/** Backend for the x86 seL4_TCB_GetBreakpoint invocation.
437 *
438 * Returns information about a particular breakpoint ID, including whether or
439 * not it's enabled.
440 *
441 * @param uds Arch TCB register context pointer.
442 * @param bp_num Hardware breakpoint ID of the BP you'd like to query.
443 * @return Structure containing information about the status of the breakpoint.
444 */
445getBreakpoint_t getBreakpoint(tcb_t *t, uint16_t bp_num)
446{
447    word_t dr7val;
448    getBreakpoint_t ret;
449    convertedTypeAndAccess_t res;
450
451    dr7val = readDr7Context(t);
452    ret.vaddr = getBpVaddrContext(t, bp_num);
453    ret.size = convertArchToSize(dr7val, bp_num);
454    res = convertArchToTypeAndAccess(dr7val, bp_num);
455    ret.type = res.type;
456    ret.rw = res.rw;
457    ret.is_enabled = breakpointIsEnabled(t, bp_num);
458    return ret;
459}
460
461/** Backend for the x86 seL4_TCB_UnsetBreakpoint invocation.
462 *
463 * Unsets and *clears* a hardware breakpoint.
464 * @param uds Arch TCB register context pointer.
465 * @param bp_num The hardware breakpoint ID you'd like to clear.
466 */
467void unsetBreakpoint(tcb_t *t, uint16_t bp_num)
468{
469    disableBreakpoint(t, bp_num);
470    unsetDr7BitsFor(t, bp_num);
471    setBpVaddrContext(t, bp_num, 0);
472}
473
474/** Used in the exception path to determine if an exception was caused by
475 * single-stepping being active.
476 *
477 * @param uc Arch TCB register context structure.
478 * @return a structure stating whether or not the exception was caused by
479 *          hardware single-stepping, and what the instruction vaddr was.
480 */
481typedef struct {
482    bool_t ret;
483    word_t instr_vaddr;
484} testAndResetSingleStepException_t;
485
486static testAndResetSingleStepException_t testAndResetSingleStepException(tcb_t *t)
487{
488    testAndResetSingleStepException_t ret;
489    word_t dr6;
490
491    dr6 = readDr6Reg();
492    if (!(dr6 & X86_DEBUG_DR6_SINGLE_STEP_FLAG)) {
493        ret.ret = false;
494        return ret;
495    }
496
497    ret.ret = true;
498    ret.instr_vaddr = t->tcbArch.tcbContext.registers[FaultIP];
499    bitwiseAndDr6Reg(~X86_DEBUG_DR6_SINGLE_STEP_FLAG);
500
501    /* And that's not all: if the breakpoint is an instruction breakpoint, we
502     * also need to set EFLAGS.RF. The processor raises the #DB exception BEFORE
503     * the instruction executes. This means that when we IRET to userspace, the
504     * SAME breakpoint will trigger again, and so on ad infinitum. EFLAGS.RF
505     * solves this problem:
506     *
507     * When EFLAGS.RF is set, the processor will ignore instruction breakpoints
508     * that should be raised, for one instruction. After that instruction
509     * executes, the processor will also automatically unset EFLAGS.RF. See
510     * Intel manuals, vol3, section 17.3.1.1.
511     */
512    /* This will automatically be popped by restore_user_context() */
513    t->tcbArch.tcbContext.registers[FLAGS] |= X86_DEBUG_EFLAGS_RESUME_FLAG;
514    return ret;
515}
516
517bool_t configureSingleStepping(tcb_t *t, uint16_t bp_num, word_t n_instr,
518                               UNUSED bool_t is_reply)
519{
520    /* On x86 no hardware breakpoints are needed for single stepping. */
521    if (n_instr == 0) {
522        /* If n_instr (number of instructions to single-step) is 0, that is the
523          * same as requesting that single-stepping be disabled.
524          */
525        t->tcbArch.tcbContext.breakpointState.single_step_enabled = false;
526        t->tcbArch.tcbContext.registers[FLAGS] &= ~X86_DEBUG_EFLAGS_TRAP_FLAG;
527    } else {
528        t->tcbArch.tcbContext.breakpointState.single_step_enabled = true;
529    }
530
531    t->tcbArch.tcbContext.breakpointState.n_instructions = n_instr;
532    return false;
533}
534
535/** Used in the exception path to determine which breakpoint triggered the
536 * exception.
537 *
538 * First, checks to see which hardware breakpoint was triggered, and saves
539 * the ID of that breakpoint. Secondly, resets that breakpoint such that its
540 * "triggered" bit is no longer in the asserted state -- whatever that means
541 * for the arch. So on x86, that means clearing the indicator bit in DR6.
542 *
543 * Aside from the ID of the breakpoint that was raised, also returns
544 * information about the breakpoint (vaddr, access, type, etc).
545 *
546 * @param uc Arch TCB register context pointer.
547 * @return Structure with a "bp_num" member that states which hardware
548 *          breakpoint was triggered, and gives information describing the
549 *          breakpoint.
550 */
551typedef struct {
552    int bp_num;
553    word_t vaddr, reason;
554} getAndResetActiveBreakpoint_t;
555
556static getAndResetActiveBreakpoint_t getAndResetActiveBreakpoint(tcb_t *t)
557{
558    convertedTypeAndAccess_t tmp;
559    getAndResetActiveBreakpoint_t ret;
560
561    /* Read from the hardware regs, not user context */
562    word_t dr6 = readDr6Reg();
563    if (dr6 & BIT(0)) {
564        ret.bp_num = 0;
565    } else if (dr6 & BIT(1)) {
566        ret.bp_num = 1;
567    } else if (dr6 & BIT(2)) {
568        ret.bp_num = 2;
569    } else if (dr6 & BIT(3)) {
570        ret.bp_num = 3;
571    } else {
572        ret.bp_num = -1;
573        return ret;
574    }
575
576    tmp = convertArchToTypeAndAccess(readDr7Context(t), ret.bp_num);
577    ret.vaddr = getBpVaddrContext(t, ret.bp_num);
578    ret.reason = tmp.type;
579
580    bitwiseAndDr6Reg(~BIT(ret.bp_num));
581    return ret;
582}
583
584exception_t handleUserLevelDebugException(int int_vector)
585{
586    tcb_t *ct;
587    getAndResetActiveBreakpoint_t active_bp;
588    testAndResetSingleStepException_t single_step_info;
589
590#if defined(CONFIG_DEBUG_BUILD) || defined(CONFIG_BENCHMARK_TRACK_KERNEL_ENTRIES)
591    ksKernelEntry.path = Entry_UserLevelFault;
592    ksKernelEntry.word = int_vector;
593#else
594    (void)int_vector;
595#endif /* DEBUG */
596
597#ifdef CONFIG_BENCHMARK_TRACK_KERNEL_ENTRIES
598    benchmark_track_start();
599#endif
600
601    ct = NODE_STATE(ksCurThread);
602
603    /* Software break request (INT3) is detected by the vector number */
604    if (int_vector == int_software_break_request) {
605        current_fault = seL4_Fault_DebugException_new(getRestartPC(NODE_STATE(ksCurThread)),
606                                                      0, seL4_SoftwareBreakRequest);
607    } else {
608        /* Hardware breakpoint trigger is detected using DR6 */
609        active_bp = getAndResetActiveBreakpoint(ct);
610        if (active_bp.bp_num >= 0) {
611            current_fault = seL4_Fault_DebugException_new(active_bp.vaddr,
612                                                          active_bp.bp_num,
613                                                          active_bp.reason);
614        } else {
615            single_step_info = testAndResetSingleStepException(ct);
616            if (single_step_info.ret == true) {
617                /* If the caller asked us to skip over N instructions before
618                 * generating the next single-step breakpoint, we shouldn't
619                 * bother to construct a fault message until we've skipped N
620                 * instructions.
621                 */
622                if (singleStepFaultCounterReady(ct) == false) {
623                    return EXCEPTION_NONE;
624                }
625                current_fault = seL4_Fault_DebugException_new(single_step_info.instr_vaddr,
626                                                              0, seL4_SingleStep);
627            } else {
628                return EXCEPTION_SYSCALL_ERROR;
629            }
630        }
631    }
632
633    handleFault(NODE_STATE(ksCurThread));
634
635    schedule();
636    activateThread();
637
638    return EXCEPTION_NONE;
639}
640
641BOOT_CODE bool_t Arch_initHardwareBreakpoints(void)
642{
643    x86_cpu_identity_t *modelinfo;
644
645    modelinfo = x86_cpuid_get_model_info();
646    /* Intel manuals, vol3, section 17.2.4, "NOTES". */
647    if (modelinfo->family == 15) {
648        if (modelinfo->model == 3 || modelinfo->model == 4
649            || modelinfo->model == 6) {
650            byte8_bps_supported = true;
651        }
652    }
653    if (modelinfo->family == 6) {
654        if (modelinfo->model == 15 || modelinfo->model == 23
655            || modelinfo->model == 0x1C) {
656            byte8_bps_supported = true;
657        }
658    }
659    return true;
660}
661
662void Arch_initBreakpointContext(user_breakpoint_state_t *uds)
663{
664    memset(uds, 0, sizeof(*uds));
665
666    /* Preload reserved values into register context */
667    uds->dr[4] = readDr6Reg() &
668                 ~(BIT(0)
669                   | BIT(1)
670                   | BIT(2)
671                   | BIT(3)
672                   | X86_DEBUG_DR6_SINGLE_STEP_FLAG);
673
674    uds->dr[5] = readDr7Reg() &
675                 ~(X86_DEBUG_BP0_ENABLE_BIT | X86_DEBUG_BP1_ENABLE_BIT
676                   | X86_DEBUG_BP2_ENABLE_BIT
677                   | X86_DEBUG_BP3_ENABLE_BIT);
678}
679
680#endif
681