#include <sys/wait.h>
 #include "ptrace.h"
 
-/* Breakpoint access modes */
-enum {
-       BP_X = 1,
-       BP_RW = 2,
-       BP_W = 4,
-};
-
-static pid_t child_pid;
-static struct ppc_debug_info dbginfo;
-
-static void get_dbginfo(void)
-{
-       int ret;
-
-       ret = ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo);
-       if (ret) {
-               perror("Can't get breakpoint info\n");
-               exit(-1);
-       }
-}
-
-static bool hwbreak_present(void)
-{
-       return (dbginfo.num_data_bps != 0);
-}
+/*
+ * Use volatile on all global var so that compiler doesn't
+ * optimise their load/stores. Otherwise selftest can fail.
+ */
+static volatile __u64 glvar;
 
-static bool dawr_present(void)
-{
-       return !!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_DAWR);
-}
+#define DAWR_MAX_LEN 512
+static volatile __u8 big_var[DAWR_MAX_LEN] __attribute__((aligned(512)));
 
-static void set_breakpoint_addr(void *addr)
-{
-       int ret;
+#define A_LEN 6
+#define B_LEN 6
+struct gstruct {
+       __u8 a[A_LEN]; /* double word aligned */
+       __u8 b[B_LEN]; /* double word unaligned */
+};
+static volatile struct gstruct gstruct __attribute__((aligned(512)));
 
-       ret = ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, addr);
-       if (ret) {
-               perror("Can't set breakpoint addr\n");
-               exit(-1);
-       }
-}
 
-static int set_hwbreakpoint_addr(void *addr, int range)
+static void get_dbginfo(pid_t child_pid, struct ppc_debug_info *dbginfo)
 {
-       int ret;
-
-       struct ppc_hw_breakpoint info;
-
-       info.version = 1;
-       info.trigger_type = PPC_BREAKPOINT_TRIGGER_RW;
-       info.addr_mode = PPC_BREAKPOINT_MODE_EXACT;
-       if (range > 0)
-               info.addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
-       info.condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
-       info.addr = (__u64)addr;
-       info.addr2 = (__u64)addr + range;
-       info.condition_value = 0;
-
-       ret = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, &info);
-       if (ret < 0) {
-               perror("Can't set breakpoint\n");
+       if (ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, dbginfo)) {
+               perror("Can't get breakpoint info");
                exit(-1);
        }
-       return ret;
 }
 
-static int del_hwbreakpoint_addr(int watchpoint_handle)
+static bool dawr_present(struct ppc_debug_info *dbginfo)
 {
-       int ret;
-
-       ret = ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, watchpoint_handle);
-       if (ret < 0) {
-               perror("Can't delete hw breakpoint\n");
-               exit(-1);
-       }
-       return ret;
+       return !!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_DAWR);
 }
 
-#define DAWR_LENGTH_MAX 512
-
-/* Dummy variables to test read/write accesses */
-static unsigned long long
-       dummy_array[DAWR_LENGTH_MAX / sizeof(unsigned long long)]
-       __attribute__((aligned(512)));
-static unsigned long long *dummy_var = dummy_array;
-
 static void write_var(int len)
 {
-       long long *plval;
-       char *pcval;
-       short *psval;
-       int *pival;
+       __u8 *pcvar;
+       __u16 *psvar;
+       __u32 *pivar;
+       __u64 *plvar;
 
        switch (len) {
        case 1:
-               pcval = (char *)dummy_var;
-               *pcval = 0xff;
+               pcvar = (__u8 *)&glvar;
+               *pcvar = 0xff;
                break;
        case 2:
-               psval = (short *)dummy_var;
-               *psval = 0xffff;
+               psvar = (__u16 *)&glvar;
+               *psvar = 0xffff;
                break;
        case 4:
-               pival = (int *)dummy_var;
-               *pival = 0xffffffff;
+               pivar = (__u32 *)&glvar;
+               *pivar = 0xffffffff;
                break;
        case 8:
-               plval = (long long *)dummy_var;
-               *plval = 0xffffffffffffffffLL;
+               plvar = (__u64 *)&glvar;
+               *plvar = 0xffffffffffffffffLL;
                break;
        }
 }
 
 static void read_var(int len)
 {
-       char cval __attribute__((unused));
-       short sval __attribute__((unused));
-       int ival __attribute__((unused));
-       long long lval __attribute__((unused));
+       __u8 cvar __attribute__((unused));
+       __u16 svar __attribute__((unused));
+       __u32 ivar __attribute__((unused));
+       __u64 lvar __attribute__((unused));
 
        switch (len) {
        case 1:
-               cval = *(char *)dummy_var;
+               cvar = (__u8)glvar;
                break;
        case 2:
-               sval = *(short *)dummy_var;
+               svar = (__u16)glvar;
                break;
        case 4:
-               ival = *(int *)dummy_var;
+               ivar = (__u32)glvar;
                break;
        case 8:
-               lval = *(long long *)dummy_var;
+               lvar = (__u64)glvar;
                break;
        }
 }
 
-/*
- * Do the r/w accesses to trigger the breakpoints. And run
- * the usual traps.
- */
-static void trigger_tests(void)
+static void test_workload(void)
 {
-       int len, ret;
+       __u8 cvar __attribute__((unused));
+       __u32 ivar __attribute__((unused));
+       int len = 0;
 
-       ret = ptrace(PTRACE_TRACEME, 0, NULL, 0);
-       if (ret) {
-               perror("Can't be traced?\n");
-               return;
+       if (ptrace(PTRACE_TRACEME, 0, NULL, 0)) {
+               perror("Child can't be traced?");
+               exit(-1);
        }
 
        /* Wake up father so that it sets up the first test */
        kill(getpid(), SIGUSR1);
 
-       /* Test write watchpoints */
-       for (len = 1; len <= sizeof(long); len <<= 1)
+       /* PTRACE_SET_DEBUGREG, WO test */
+       for (len = 1; len <= sizeof(glvar); len <<= 1)
                write_var(len);
 
-       /* Test read/write watchpoints (on read accesses) */
-       for (len = 1; len <= sizeof(long); len <<= 1)
+       /* PTRACE_SET_DEBUGREG, RO test */
+       for (len = 1; len <= sizeof(glvar); len <<= 1)
                read_var(len);
 
-       /* Test when breakpoint is unset */
-
-       /* Test write watchpoints */
-       for (len = 1; len <= sizeof(long); len <<= 1)
-               write_var(len);
+       /* PTRACE_SET_DEBUGREG, RW test */
+       for (len = 1; len <= sizeof(glvar); len <<= 1) {
+               if (rand() % 2)
+                       read_var(len);
+               else
+                       write_var(len);
+       }
 
-       /* Test read/write watchpoints (on read accesses) */
-       for (len = 1; len <= sizeof(long); len <<= 1)
-               read_var(len);
+       /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */
+       write_var(1);
+
+       /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */
+       read_var(1);
+
+       /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */
+       if (rand() % 2)
+               write_var(1);
+       else
+               read_var(1);
+
+       /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */
+       gstruct.a[rand() % A_LEN] = 'a';
+
+       /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */
+       cvar = gstruct.a[rand() % A_LEN];
+
+       /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */
+       if (rand() % 2)
+               gstruct.a[rand() % A_LEN] = 'a';
+       else
+               cvar = gstruct.a[rand() % A_LEN];
+
+       /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */
+       gstruct.b[rand() % B_LEN] = 'b';
+
+       /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */
+       cvar = gstruct.b[rand() % B_LEN];
+
+       /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */
+       if (rand() % 2)
+               gstruct.b[rand() % B_LEN] = 'b';
+       else
+               cvar = gstruct.b[rand() % B_LEN];
+
+       /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */
+       if (rand() % 2)
+               *((int *)(gstruct.a + 4)) = 10;
+       else
+               ivar = *((int *)(gstruct.a + 4));
+
+       /* PPC_PTRACE_SETHWDEBUG. DAWR_MAX_LEN. RW test */
+       if (rand() % 2)
+               big_var[rand() % DAWR_MAX_LEN] = 'a';
+       else
+               cvar = big_var[rand() % DAWR_MAX_LEN];
 }
 
-static void check_success(const char *msg)
+static void check_success(pid_t child_pid, const char *name, const char *type,
+                         unsigned long saddr, int len)
 {
-       const char *msg2;
        int status;
+       siginfo_t siginfo;
+       unsigned long eaddr = (saddr + len - 1) | 0x7;
+
+       saddr &= ~0x7;
 
        /* Wait for the child to SIGTRAP */
        wait(&status);
 
-       msg2 = "Failed";
+       ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &siginfo);
 
-       if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
-               msg2 = "Child process hit the breakpoint";
+       if (!WIFSTOPPED(status) || WSTOPSIG(status) != SIGTRAP ||
+           (unsigned long)siginfo.si_addr < saddr ||
+           (unsigned long)siginfo.si_addr > eaddr) {
+               printf("%s, %s, len: %d: Fail\n", name, type, len);
+               exit(-1);
        }
 
-       printf("%s Result: [%s]\n", msg, msg2);
+       printf("%s, %s, len: %d: Ok\n", name, type, len);
+
+       /*
+        * For ptrace registered watchpoint, signal is generated
+        * before executing load/store. Singlestep the instruction
+        * and then continue the test.
+        */
+       ptrace(PTRACE_SINGLESTEP, child_pid, NULL, 0);
+       wait(NULL);
 }
 
-static void launch_watchpoints(char *buf, int mode, int len,
-                              struct ppc_debug_info *dbginfo, bool dawr)
+static void ptrace_set_debugreg(pid_t child_pid, unsigned long wp_addr)
 {
-       const char *mode_str;
-       unsigned long data = (unsigned long)(dummy_var);
-       int wh, range;
-
-       data &= ~0x7UL;
-
-       if (mode == BP_W) {
-               data |= (1UL << 1);
-               mode_str = "write";
-       } else {
-               data |= (1UL << 0);
-               data |= (1UL << 1);
-               mode_str = "read";
+       if (ptrace(PTRACE_SET_DEBUGREG, child_pid, 0, wp_addr)) {
+               perror("PTRACE_SET_DEBUGREG failed");
+               exit(-1);
        }
+}
 
-       /* Set DABR_TRANSLATION bit */
-       data |= (1UL << 2);
-
-       /* use PTRACE_SET_DEBUGREG breakpoints */
-       set_breakpoint_addr((void *)data);
-       ptrace(PTRACE_CONT, child_pid, NULL, 0);
-       sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len);
-       check_success(buf);
-       /* Unregister hw brkpoint */
-       set_breakpoint_addr(NULL);
+static int ptrace_sethwdebug(pid_t child_pid, struct ppc_hw_breakpoint *info)
+{
+       int wh = ptrace(PPC_PTRACE_SETHWDEBUG, child_pid, 0, info);
 
-       data = (data & ~7); /* remove dabr control bits */
+       if (wh <= 0) {
+               perror("PPC_PTRACE_SETHWDEBUG failed");
+               exit(-1);
+       }
+       return wh;
+}
 
-       /* use PPC_PTRACE_SETHWDEBUG breakpoint */
-       if (!(dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE))
-               return; /* not supported */
-       wh = set_hwbreakpoint_addr((void *)data, 0);
-       ptrace(PTRACE_CONT, child_pid, NULL, 0);
-       sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len);
-       check_success(buf);
-       /* Unregister hw brkpoint */
-       del_hwbreakpoint_addr(wh);
-
-       /* try a wider range */
-       range = 8;
-       if (dawr)
-               range = 512 - ((int)data & (DAWR_LENGTH_MAX - 1));
-       wh = set_hwbreakpoint_addr((void *)data, range);
-       ptrace(PTRACE_CONT, child_pid, NULL, 0);
-       sprintf(buf, "Test %s watchpoint with len: %d ", mode_str, len);
-       check_success(buf);
-       /* Unregister hw brkpoint */
-       del_hwbreakpoint_addr(wh);
+static void ptrace_delhwdebug(pid_t child_pid, int wh)
+{
+       if (ptrace(PPC_PTRACE_DELHWDEBUG, child_pid, 0, wh) < 0) {
+               perror("PPC_PTRACE_DELHWDEBUG failed");
+               exit(-1);
+       }
 }
 
-/* Set the breakpoints and check the child successfully trigger them */
-static int launch_tests(bool dawr)
+#define DABR_READ_SHIFT                0
+#define DABR_WRITE_SHIFT       1
+#define DABR_TRANSLATION_SHIFT 2
+
+static int test_set_debugreg(pid_t child_pid)
 {
-       char buf[1024];
-       int len, i, status;
+       unsigned long wp_addr = (unsigned long)&glvar;
+       char *name = "PTRACE_SET_DEBUGREG";
+       int len;
+
+       /* PTRACE_SET_DEBUGREG, WO test*/
+       wp_addr &= ~0x7UL;
+       wp_addr |= (1UL << DABR_WRITE_SHIFT);
+       wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
+       for (len = 1; len <= sizeof(glvar); len <<= 1) {
+               ptrace_set_debugreg(child_pid, wp_addr);
+               ptrace(PTRACE_CONT, child_pid, NULL, 0);
+               check_success(child_pid, name, "WO", wp_addr, len);
+       }
 
-       struct ppc_debug_info dbginfo;
+       /* PTRACE_SET_DEBUGREG, RO test */
+       wp_addr &= ~0x7UL;
+       wp_addr |= (1UL << DABR_READ_SHIFT);
+       wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
+       for (len = 1; len <= sizeof(glvar); len <<= 1) {
+               ptrace_set_debugreg(child_pid, wp_addr);
+               ptrace(PTRACE_CONT, child_pid, NULL, 0);
+               check_success(child_pid, name, "RO", wp_addr, len);
+       }
 
-       i = ptrace(PPC_PTRACE_GETHWDBGINFO, child_pid, NULL, &dbginfo);
-       if (i) {
-               perror("Can't set breakpoint info\n");
-               exit(-1);
+       /* PTRACE_SET_DEBUGREG, RW test */
+       wp_addr &= ~0x7UL;
+       wp_addr |= (1Ul << DABR_READ_SHIFT);
+       wp_addr |= (1UL << DABR_WRITE_SHIFT);
+       wp_addr |= (1UL << DABR_TRANSLATION_SHIFT);
+       for (len = 1; len <= sizeof(glvar); len <<= 1) {
+               ptrace_set_debugreg(child_pid, wp_addr);
+               ptrace(PTRACE_CONT, child_pid, NULL, 0);
+               check_success(child_pid, name, "RW", wp_addr, len);
        }
-       if (!(dbginfo.features & PPC_DEBUG_FEATURE_DATA_BP_RANGE))
-               printf("WARNING: Kernel doesn't support PPC_PTRACE_SETHWDEBUG\n");
 
-       /* Write watchpoint */
-       for (len = 1; len <= sizeof(long); len <<= 1)
-               launch_watchpoints(buf, BP_W, len, &dbginfo, dawr);
+       ptrace_set_debugreg(child_pid, 0);
+       return 0;
+}
 
-       /* Read-Write watchpoint */
-       for (len = 1; len <= sizeof(long); len <<= 1)
-               launch_watchpoints(buf, BP_RW, len, &dbginfo, dawr);
+static void get_ppc_hw_breakpoint(struct ppc_hw_breakpoint *info, int type,
+                                 unsigned long addr, int len)
+{
+       info->version = 1;
+       info->trigger_type = type;
+       info->condition_mode = PPC_BREAKPOINT_CONDITION_NONE;
+       info->addr = (__u64)addr;
+       info->addr2 = (__u64)addr + len;
+       info->condition_value = 0;
+       if (!len)
+               info->addr_mode = PPC_BREAKPOINT_MODE_EXACT;
+       else
+               info->addr_mode = PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE;
+}
 
+static void test_sethwdebug_exact(pid_t child_pid)
+{
+       struct ppc_hw_breakpoint info;
+       unsigned long wp_addr = (unsigned long)&glvar;
+       char *name = "PPC_PTRACE_SETHWDEBUG, MODE_EXACT";
+       int len = 1; /* hardcoded in kernel */
+       int wh;
+
+       /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, WO test */
+       get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, 0);
+       wh = ptrace_sethwdebug(child_pid, &info);
        ptrace(PTRACE_CONT, child_pid, NULL, 0);
+       check_success(child_pid, name, "WO", wp_addr, len);
+       ptrace_delhwdebug(child_pid, wh);
 
-       /*
-        * Now we have unregistered the breakpoint, access by child
-        * should not cause SIGTRAP.
-        */
+       /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RO test */
+       get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, 0);
+       wh = ptrace_sethwdebug(child_pid, &info);
+       ptrace(PTRACE_CONT, child_pid, NULL, 0);
+       check_success(child_pid, name, "RO", wp_addr, len);
+       ptrace_delhwdebug(child_pid, wh);
 
-       wait(&status);
+       /* PPC_PTRACE_SETHWDEBUG, MODE_EXACT, RW test */
+       get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, 0);
+       wh = ptrace_sethwdebug(child_pid, &info);
+       ptrace(PTRACE_CONT, child_pid, NULL, 0);
+       check_success(child_pid, name, "RW", wp_addr, len);
+       ptrace_delhwdebug(child_pid, wh);
+}
 
-       if (WIFSTOPPED(status) && WSTOPSIG(status) == SIGTRAP) {
-               printf("FAIL: Child process hit the breakpoint, which is not expected\n");
-               ptrace(PTRACE_CONT, child_pid, NULL, 0);
-               return TEST_FAIL;
-       }
+static void test_sethwdebug_range_aligned(pid_t child_pid)
+{
+       struct ppc_hw_breakpoint info;
+       unsigned long wp_addr;
+       char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED";
+       int len;
+       int wh;
+
+       /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, WO test */
+       wp_addr = (unsigned long)&gstruct.a;
+       len = A_LEN;
+       get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
+       wh = ptrace_sethwdebug(child_pid, &info);
+       ptrace(PTRACE_CONT, child_pid, NULL, 0);
+       check_success(child_pid, name, "WO", wp_addr, len);
+       ptrace_delhwdebug(child_pid, wh);
+
+       /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RO test */
+       wp_addr = (unsigned long)&gstruct.a;
+       len = A_LEN;
+       get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len);
+       wh = ptrace_sethwdebug(child_pid, &info);
+       ptrace(PTRACE_CONT, child_pid, NULL, 0);
+       check_success(child_pid, name, "RO", wp_addr, len);
+       ptrace_delhwdebug(child_pid, wh);
+
+       /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW ALIGNED, RW test */
+       wp_addr = (unsigned long)&gstruct.a;
+       len = A_LEN;
+       get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
+       wh = ptrace_sethwdebug(child_pid, &info);
+       ptrace(PTRACE_CONT, child_pid, NULL, 0);
+       check_success(child_pid, name, "RW", wp_addr, len);
+       ptrace_delhwdebug(child_pid, wh);
+}
 
-       if (WIFEXITED(status))
-               printf("Child exited normally\n");
+static void test_sethwdebug_range_unaligned(pid_t child_pid)
+{
+       struct ppc_hw_breakpoint info;
+       unsigned long wp_addr;
+       char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED";
+       int len;
+       int wh;
+
+       /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, WO test */
+       wp_addr = (unsigned long)&gstruct.b;
+       len = B_LEN;
+       get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
+       wh = ptrace_sethwdebug(child_pid, &info);
+       ptrace(PTRACE_CONT, child_pid, NULL, 0);
+       check_success(child_pid, name, "WO", wp_addr, len);
+       ptrace_delhwdebug(child_pid, wh);
+
+       /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RO test */
+       wp_addr = (unsigned long)&gstruct.b;
+       len = B_LEN;
+       get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_READ, wp_addr, len);
+       wh = ptrace_sethwdebug(child_pid, &info);
+       ptrace(PTRACE_CONT, child_pid, NULL, 0);
+       check_success(child_pid, name, "RO", wp_addr, len);
+       ptrace_delhwdebug(child_pid, wh);
+
+       /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, RW test */
+       wp_addr = (unsigned long)&gstruct.b;
+       len = B_LEN;
+       get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
+       wh = ptrace_sethwdebug(child_pid, &info);
+       ptrace(PTRACE_CONT, child_pid, NULL, 0);
+       check_success(child_pid, name, "RW", wp_addr, len);
+       ptrace_delhwdebug(child_pid, wh);
 
-       return TEST_PASS;
+}
+
+static void test_sethwdebug_range_unaligned_dar(pid_t child_pid)
+{
+       struct ppc_hw_breakpoint info;
+       unsigned long wp_addr;
+       char *name = "PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE";
+       int len;
+       int wh;
+
+       /* PPC_PTRACE_SETHWDEBUG, MODE_RANGE, DW UNALIGNED, DAR OUTSIDE, RW test */
+       wp_addr = (unsigned long)&gstruct.b;
+       len = B_LEN;
+       get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_WRITE, wp_addr, len);
+       wh = ptrace_sethwdebug(child_pid, &info);
+       ptrace(PTRACE_CONT, child_pid, NULL, 0);
+       check_success(child_pid, name, "RW", wp_addr, len);
+       ptrace_delhwdebug(child_pid, wh);
+}
+
+static void test_sethwdebug_dawr_max_range(pid_t child_pid)
+{
+       struct ppc_hw_breakpoint info;
+       unsigned long wp_addr;
+       char *name = "PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN";
+       int len;
+       int wh;
+
+       /* PPC_PTRACE_SETHWDEBUG, DAWR_MAX_LEN, RW test */
+       wp_addr = (unsigned long)big_var;
+       len = DAWR_MAX_LEN;
+       get_ppc_hw_breakpoint(&info, PPC_BREAKPOINT_TRIGGER_RW, wp_addr, len);
+       wh = ptrace_sethwdebug(child_pid, &info);
+       ptrace(PTRACE_CONT, child_pid, NULL, 0);
+       check_success(child_pid, name, "RW", wp_addr, len);
+       ptrace_delhwdebug(child_pid, wh);
+}
+
+/* Set the breakpoints and check the child successfully trigger them */
+static void
+run_tests(pid_t child_pid, struct ppc_debug_info *dbginfo, bool dawr)
+{
+       test_set_debugreg(child_pid);
+       if (dbginfo->features & PPC_DEBUG_FEATURE_DATA_BP_RANGE) {
+               test_sethwdebug_exact(child_pid);
+               test_sethwdebug_range_aligned(child_pid);
+               if (dawr) {
+                       test_sethwdebug_range_unaligned(child_pid);
+                       test_sethwdebug_range_unaligned_dar(child_pid);
+                       test_sethwdebug_dawr_max_range(child_pid);
+               }
+       }
 }
 
 static int ptrace_hwbreak(void)
 {
-       pid_t pid;
-       int ret;
+       pid_t child_pid;
+       struct ppc_debug_info dbginfo;
        bool dawr;
 
-       pid = fork();
-       if (!pid) {
-               trigger_tests();
+       child_pid = fork();
+       if (!child_pid) {
+               test_workload();
                return 0;
        }
 
        wait(NULL);
 
-       child_pid = pid;
-
-       get_dbginfo();
-       SKIP_IF(!hwbreak_present());
-       dawr = dawr_present();
+       get_dbginfo(child_pid, &dbginfo);
+       SKIP_IF(dbginfo.num_data_bps == 0);
 
-       ret = launch_tests(dawr);
+       dawr = dawr_present(&dbginfo);
+       run_tests(child_pid, &dbginfo, dawr);
 
+       /* Let the child exit first. */
+       ptrace(PTRACE_CONT, child_pid, NULL, 0);
        wait(NULL);
 
-       return ret;
+       /*
+        * Testcases exits immediately with -1 on any failure. If
+        * it has reached here, it means all tests were successful.
+        */
+       return TEST_PASS;
 }
 
 int main(int argc, char **argv, char **envp)