| 1 | /* |
| 2 | * ========== Copyright Header Begin ========================================== |
| 3 | * |
| 4 | * OpenSPARC T2 Processor File: lockstep.c |
| 5 | * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. |
| 6 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES. |
| 7 | * |
| 8 | * The above named program is free software; you can redistribute it and/or |
| 9 | * modify it under the terms of the GNU General Public |
| 10 | * License version 2 as published by the Free Software Foundation. |
| 11 | * |
| 12 | * The above named program is distributed in the hope that it will be |
| 13 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 | * General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public |
| 18 | * License along with this work; if not, write to the Free Software |
| 19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. |
| 20 | * |
| 21 | * ========== Copyright Header End ============================================ |
| 22 | */ |
| 23 | /* |
| 24 | * Copyright 2006 Sun Microsystems, Inc. All rights reserved. |
| 25 | * Use is subject to license terms. |
| 26 | */ |
| 27 | #pragma ident "@(#)lockstep.c 1.5 06/10/25 SMI" |
| 28 | |
| 29 | #include <stdio.h> |
| 30 | #include <stdlib.h> |
| 31 | #include <unistd.h> |
| 32 | #include <sys/types.h> |
| 33 | #include <sys/ipc.h> |
| 34 | #include <sys/shm.h> |
| 35 | #include <assert.h> |
| 36 | #include <string.h> |
| 37 | #include <strings.h> |
| 38 | |
| 39 | #include "ss_common.h" |
| 40 | #include "niagara.h" |
| 41 | |
| 42 | #if 1 |
| 43 | #define DBGP(s) do { s } while (0) |
| 44 | #endif |
| 45 | |
| 46 | void dbg_lockstep(simcpu_t *sp, uint32_t rawi); |
| 47 | void dbg_lockstep_parse(void); |
| 48 | void dbg_lockstep_dump(void); |
| 49 | |
| 50 | static void dump_mismatch_data(simcpu_t *sp); |
| 51 | static void dump_instn_data(simcpu_t *sp); |
| 52 | static void dump_intregs(uint64_t *data); |
| 53 | static void diff_intregs(uint64_t *intrega, uint64_t *intregb); |
| 54 | |
| 55 | #define MASTER 0xfeedface |
| 56 | #define SLAVE 0xdeadbeef |
| 57 | |
| 58 | typedef char decoded_instn_str_t[256]; |
| 59 | |
| 60 | uint64_t start_instn = 0x0; |
| 61 | bool_t trace_all = false; |
| 62 | bool_t is_master = false; |
| 63 | uint64_t current_instn = 0x0; |
| 64 | |
| 65 | typedef struct INSTN_DATA { |
| 66 | uint64_t pc; |
| 67 | uint64_t npc; |
| 68 | uint64_t instn_cnt; |
| 69 | uint32_t rawi; |
| 70 | uint64_t intreg[NINT]; |
| 71 | uint64_t cycle_target; |
| 72 | } instn_data_t; |
| 73 | |
| 74 | typedef struct LOCKSTEP_DATA_T { |
| 75 | int flag; |
| 76 | instn_data_t prev_master; |
| 77 | instn_data_t master; |
| 78 | instn_data_t prev_slave; |
| 79 | instn_data_t slave; |
| 80 | } lockstep_data_t; |
| 81 | |
| 82 | #define SHM_ID (getuid() | ('F'<<24)) |
| 83 | int shmfd; |
| 84 | lockstep_data_t *ctrl_datap = NULL; |
| 85 | |
| 86 | /* |
| 87 | * This will be called while parsing the conf file |
| 88 | * to allow this hook to parse specific options from |
| 89 | * the conf file. |
| 90 | * |
| 91 | * Format of lockstep directive is: |
| 92 | * debug_hook [lockstep] [mode] [start] |
| 93 | * |
| 94 | * [lockstep] - we've already parsed this. That's how we got here. |
| 95 | * [mode] - master or slave |
| 96 | * master writes its data to shm region and waits for |
| 97 | * slave to compare. Dump any mismatches when simulators |
| 98 | * fall out of lockstep. |
| 99 | * [start] - optional, when supplied we enter into lockstep mode |
| 100 | * from this instn count onwards |
| 101 | * |
| 102 | * We need to create a shared mem region to store the lockstep |
| 103 | * buffer so that the slave can see the %pc and reg state of the |
| 104 | * master and compare against its %pc and reg state. |
| 105 | */ |
| 106 | void |
| 107 | dbg_lockstep_parse(void) |
| 108 | { |
| 109 | lexer_tok_t tok; |
| 110 | int shmflg; |
| 111 | uint32_t shm_size = sizeof (lockstep_data_t); |
| 112 | |
| 113 | |
| 114 | DBGP(printf("\nInside dbg_lockstep_parse()");); |
| 115 | |
| 116 | /* Parse mode */ |
| 117 | tok = lex_get_token(); |
| 118 | |
| 119 | if (streq(lex.strp, "master")) |
| 120 | is_master = true; |
| 121 | else if (streq(lex.strp, "slave")) |
| 122 | is_master = false; |
| 123 | else |
| 124 | lex_fatal("Unknown lockstep mode [%s]\n", lex.strp); |
| 125 | |
| 126 | /* |
| 127 | * see if next option is a ';' - otherwise parse star_instn |
| 128 | */ |
| 129 | tok = lex_get_token(); |
| 130 | if (tok == T_S_Colon) { |
| 131 | trace_all = true; /* no start instn so check all instns */ |
| 132 | lex_unget(); |
| 133 | } else { |
| 134 | lex_unget(); |
| 135 | lex_get(T_Number); /* parse start */ |
| 136 | start_instn = lex.val; |
| 137 | |
| 138 | trace_all = false; |
| 139 | |
| 140 | DBGP(printf("\nlockstep: start_instn=0x%llx", start_instn);); |
| 141 | } |
| 142 | |
| 143 | /* |
| 144 | * Setup shared mem segment |
| 145 | */ |
| 146 | if (is_master) |
| 147 | shmflg = 0777 | IPC_CREAT; |
| 148 | else |
| 149 | shmflg = 0777; |
| 150 | |
| 151 | if ((shmfd = shmget(SHM_ID, shm_size, shmflg)) < 0) { |
| 152 | perror("shmget"); |
| 153 | exit(1); |
| 154 | } |
| 155 | |
| 156 | |
| 157 | if ((ctrl_datap = (lockstep_data_t *)shmat(shmfd, NULL, SHM_RND)) |
| 158 | == (lockstep_data_t *)-1) { |
| 159 | perror("shmat"); |
| 160 | exit(1); |
| 161 | } |
| 162 | |
| 163 | if (is_master) { |
| 164 | memset(ctrl_datap, 0, sizeof (lockstep_data_t)); |
| 165 | ctrl_datap->flag = MASTER; /* allow master to start */ |
| 166 | } |
| 167 | |
| 168 | |
| 169 | |
| 170 | /* |
| 171 | * return to parse_debug_hook() which will take care of |
| 172 | * parsing the last semi colon. |
| 173 | */ |
| 174 | } |
| 175 | |
| 176 | /* |
| 177 | * This function will get called before each instruction |
| 178 | * gets executed in execloop(). |
| 179 | */ |
| 180 | void |
| 181 | dbg_lockstep(simcpu_t *sp, uint32_t rawi) |
| 182 | { |
| 183 | sparcv9_cpu_t *v9p; |
| 184 | |
| 185 | current_instn++; |
| 186 | |
| 187 | /* Check to see if we are comparing instns yet */ |
| 188 | if (trace_all == false) { |
| 189 | if (sp->cycle < start_instn) |
| 190 | return; |
| 191 | } |
| 192 | |
| 193 | if (sp->cycle == start_instn) { |
| 194 | printf("lockstep: Turned on lockstep mode on instn=0x%llx", |
| 195 | start_instn); fflush(stdout); |
| 196 | } |
| 197 | |
| 198 | while (1) { |
| 199 | if (is_master) { |
| 200 | /* |
| 201 | * MASTER Loop, entered on every instruction |
| 202 | */ |
| 203 | if (ctrl_datap->flag == MASTER) { /* wait for flag to be set */ |
| 204 | |
| 205 | /* write master %pc etc. to shared mem */ |
| 206 | ctrl_datap->master.pc = sp->pc; |
| 207 | ctrl_datap->master.npc = sp->npc; |
| 208 | ctrl_datap->master.instn_cnt = sp->total_instr; |
| 209 | ctrl_datap->master.rawi = rawi; |
| 210 | bcopy(sp->intreg, ctrl_datap->master.intreg, |
| 211 | (sizeof (uint64_t)) * NINT); |
| 212 | ctrl_datap->master.cycle_target = sp->cycle_target; |
| 213 | |
| 214 | /* Signal slave that master has written data */ |
| 215 | ctrl_datap->flag = SLAVE; |
| 216 | |
| 217 | /* Hand cpu over to slave */ |
| 218 | yield(); |
| 219 | |
| 220 | /* When cpu comes back, we return to exec_loop */ |
| 221 | return; |
| 222 | } /* if */ |
| 223 | } else { |
| 224 | /* |
| 225 | * SLAVE Loop |
| 226 | */ |
| 227 | if (ctrl_datap->flag == SLAVE) { |
| 228 | |
| 229 | /* |
| 230 | * Save slave data - so we can compare current data with |
| 231 | * data from previous instn. |
| 232 | */ |
| 233 | ctrl_datap->slave.pc = sp->pc; |
| 234 | ctrl_datap->slave.npc = sp->npc; |
| 235 | ctrl_datap->slave.instn_cnt = sp->total_instr; |
| 236 | bcopy(sp->intreg, ctrl_datap->slave.intreg, |
| 237 | (sizeof (uint64_t)) * NINT); |
| 238 | ctrl_datap->slave.rawi = rawi; |
| 239 | ctrl_datap->slave.cycle_target = sp->cycle_target; |
| 240 | |
| 241 | /* |
| 242 | * Compare Master instns and data with Slave instns |
| 243 | * and data. If they don't match, we have a problem!! |
| 244 | */ |
| 245 | |
| 246 | if (bcmp(&ctrl_datap->master, &ctrl_datap->slave, |
| 247 | sizeof (instn_data_t)) != 0) { |
| 248 | dump_mismatch_data(sp); |
| 249 | exit(1); |
| 250 | } |
| 251 | |
| 252 | /* Save master data to prev_master */ |
| 253 | bcopy(&ctrl_datap->master, &ctrl_datap->prev_master, |
| 254 | sizeof (instn_data_t)); |
| 255 | |
| 256 | /* Save slave data to prev_slave */ |
| 257 | bcopy(&ctrl_datap->slave, &ctrl_datap->prev_slave, |
| 258 | sizeof (instn_data_t)); |
| 259 | |
| 260 | /* Signal master that slave has written data */ |
| 261 | ctrl_datap->flag = MASTER; |
| 262 | |
| 263 | /* Give up the cpu so master can do some work */ |
| 264 | yield(); |
| 265 | |
| 266 | /* When cpu comes back, we return to exec_loop */ |
| 267 | return; |
| 268 | } /* if */ |
| 269 | |
| 270 | } /* else */ |
| 271 | |
| 272 | /* |
| 273 | * Rather than have the MASTER legion spin in this while loop |
| 274 | * while the SLAVE is comparing data, why not hand the cpu |
| 275 | * over to the SLAVE process so it can complete it's work |
| 276 | * and return the flag to the MASTER legion. |
| 277 | */ |
| 278 | yield(); |
| 279 | } /* while */ |
| 280 | } |
| 281 | |
| 282 | void |
| 283 | dbg_lockstep_dump(void) |
| 284 | { |
| 285 | printf("\ndbg_lockstep_dump: mode=[%s] start=0x%llx instn_count=[0x%llx]", |
| 286 | is_master ? "master" : "slave", start_instn, current_instn); |
| 287 | } |
| 288 | |
| 289 | static void |
| 290 | dump_intregs(uint64_t *intreg) |
| 291 | { |
| 292 | int i; |
| 293 | |
| 294 | for (i = 0; i < 8; i++) { |
| 295 | printf("\ng%d=0x%016llx o%d=0x%016llx l%d=0x%016llx i%d=0x%016llx", |
| 296 | i, intreg[i], |
| 297 | i, intreg[i+8], |
| 298 | i, intreg[i+16], |
| 299 | i, intreg[i+24]); |
| 300 | } |
| 301 | } |
| 302 | |
| 303 | static void |
| 304 | diff_intregs(uint64_t *intrega, uint64_t *intregb) |
| 305 | { |
| 306 | int i; |
| 307 | |
| 308 | for (i = 0; i < 8; i++) { |
| 309 | if (intrega[i] != intregb[i]) { |
| 310 | printf("\n --> g%d=0x%016llx != g%d=0x%016llx", |
| 311 | i, intrega[i], i, intregb[i]); |
| 312 | } |
| 313 | if (intrega[i+8] != intregb[i+8]) { |
| 314 | printf("\n --> o%d=0x%016llx != o%d=0x%016llx", |
| 315 | i, intrega[i+8], i, intregb[i+8]); |
| 316 | } |
| 317 | if (intrega[i+16] != intregb[i+16]) { |
| 318 | printf("\n --> l%d=0x%016llx != l%d=0x%016llx", |
| 319 | i, intrega[i+16], i, intregb[i+16]); |
| 320 | } |
| 321 | if (intrega[i+24] != intregb[i+24]) { |
| 322 | printf("\n --> i%d=0x%016llx != i%d=0x%016llx", |
| 323 | i, intrega[i+24], i, intregb[i+24]); |
| 324 | } |
| 325 | } |
| 326 | } |
| 327 | |
| 328 | static void |
| 329 | dump_mismatch_data(simcpu_t *sp) |
| 330 | { |
| 331 | |
| 332 | printf("\nERROR: Instn OR Data Mismatch !!!"); |
| 333 | |
| 334 | /* XXX FIXME: could dump out deubg_log if there is one here */ |
| 335 | |
| 336 | printf("\nMismatch Instn (has not been executed yet) marked with ->"); |
| 337 | dump_instn_data(sp); |
| 338 | |
| 339 | printf("\nMaster registers:"); |
| 340 | dump_intregs(ctrl_datap->master.intreg); |
| 341 | |
| 342 | printf("\nSlave registers:"); |
| 343 | dump_intregs(sp->intreg); |
| 344 | |
| 345 | printf("\nDiffs:"); |
| 346 | diff_intregs(ctrl_datap->master.intreg, sp->intreg); |
| 347 | } |
| 348 | |
| 349 | static void |
| 350 | dump_instn_data(simcpu_t *sp) |
| 351 | { |
| 352 | |
| 353 | |
| 354 | decoded_instn_str_t master_instn_str; |
| 355 | decoded_instn_str_t prev_master_instn_str; |
| 356 | decoded_instn_str_t slave_instn_str; |
| 357 | decoded_instn_str_t prev_slave_instn_str; |
| 358 | |
| 359 | sparcv9_idis(master_instn_str, |
| 360 | sizeof (decoded_instn_str_t), |
| 361 | ctrl_datap->master.rawi, sp->pc); |
| 362 | |
| 363 | sparcv9_idis(prev_master_instn_str, |
| 364 | sizeof (decoded_instn_str_t), |
| 365 | ctrl_datap->prev_master.rawi, sp->pc); |
| 366 | |
| 367 | sparcv9_idis(slave_instn_str, |
| 368 | sizeof (decoded_instn_str_t), |
| 369 | ctrl_datap->slave.rawi, sp->pc); |
| 370 | |
| 371 | sparcv9_idis(prev_slave_instn_str, |
| 372 | sizeof (decoded_instn_str_t), |
| 373 | ctrl_datap->prev_slave.rawi, sp->pc); |
| 374 | |
| 375 | printf("\n" \ |
| 376 | " master instn#[0x%llx] pc[0x%llx] rawi[0x%08x] npc[0x%llx] "\ |
| 377 | "target[0x%llx] - [%s]\n" \ |
| 378 | " slave instn#[0x%llx] pc[0x%llx] rawi[0x%08x] npc[0x%llx] "\ |
| 379 | "target[0x%llx] - [%s]\n" \ |
| 380 | "-> master instn#[0x%llx] pc[0x%llx] rawi[0x%08x] npc[0x%llx] "\ |
| 381 | "target[0x%llx] - [%s]\n" \ |
| 382 | "-> slave instn#[0x%llx] pc[0x%llx] rawi[0x%08x] npc[0x%llx] "\ |
| 383 | "target[0x%llx] - [%s]", |
| 384 | |
| 385 | ctrl_datap->prev_master.instn_cnt, |
| 386 | ctrl_datap->prev_master.pc, |
| 387 | ctrl_datap->prev_master.rawi, |
| 388 | ctrl_datap->prev_master.npc, |
| 389 | ctrl_datap->prev_master.cycle_target, |
| 390 | prev_master_instn_str, |
| 391 | |
| 392 | ctrl_datap->prev_slave.instn_cnt, |
| 393 | ctrl_datap->prev_slave.pc, |
| 394 | ctrl_datap->prev_slave.rawi, |
| 395 | ctrl_datap->prev_slave.npc, |
| 396 | ctrl_datap->prev_slave.cycle_target, |
| 397 | prev_slave_instn_str, |
| 398 | |
| 399 | ctrl_datap->master.instn_cnt, |
| 400 | ctrl_datap->master.pc, |
| 401 | ctrl_datap->master.rawi, |
| 402 | ctrl_datap->master.npc, |
| 403 | ctrl_datap->master.cycle_target, |
| 404 | master_instn_str, |
| 405 | |
| 406 | ctrl_datap->slave.instn_cnt, |
| 407 | ctrl_datap->slave.pc, |
| 408 | ctrl_datap->slave.rawi, |
| 409 | ctrl_datap->slave.npc, |
| 410 | ctrl_datap->slave.cycle_target, |
| 411 | slave_instn_str); |
| 412 | } |