blob: 65210737b2fd20ab637efd3915d4e18ce8116406 [file] [log] [blame]
bartbedfd232009-03-26 19:07:15 +00001/* -*- mode: C; c-basic-offset: 3; -*- */
bart09dc13f2009-02-14 15:13:31 +00002/*
bart86562bd2009-02-16 19:43:56 +00003 This file is part of drd, a thread error detector.
bart09dc13f2009-02-14 15:13:31 +00004
5 Copyright (C) 2006-2009 Bart Van Assche <bart.vanassche@gmail.com>.
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful, but
13 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 License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307, USA.
21
22 The GNU General Public License is contained in the file COPYING.
23*/
24
25
26#include "drd_bitmap.h"
27#include "drd_thread_bitmap.h"
bart41b226c2009-02-14 16:55:19 +000028#include "drd_vc.h" /* DRD_(vc_snprint)() */
bart09dc13f2009-02-14 15:13:31 +000029
30/* Include several source files here in order to allow the compiler to */
31/* do more inlining. */
32#include "drd_bitmap.c"
33#include "drd_load_store.h"
34#include "drd_segment.c"
35#include "drd_thread.c"
36#include "drd_vc.c"
37#include "libvex_guest_offsets.h"
38
39
40/* STACK_POINTER_OFFSET: VEX register offset for the stack pointer register. */
41#if defined(VGA_x86)
42#define STACK_POINTER_OFFSET OFFSET_x86_ESP
43#elif defined(VGA_amd64)
44#define STACK_POINTER_OFFSET OFFSET_amd64_RSP
45#elif defined(VGA_ppc32)
46#define STACK_POINTER_OFFSET ((OFFSET_ppc32_GPR0 + OFFSET_ppc32_GPR2) / 2)
47#elif defined(VGA_ppc64)
48#define STACK_POINTER_OFFSET ((OFFSET_ppc64_GPR0 + OFFSET_ppc64_GPR2) / 2)
49#else
50#error Unknown architecture.
51#endif
52
53
54/* Local variables. */
55
bart62a784c2009-02-15 13:11:14 +000056static Bool DRD_(s_check_stack_accesses) = False;
bart09dc13f2009-02-14 15:13:31 +000057
58
59/* Function definitions. */
60
61Bool DRD_(get_check_stack_accesses)()
62{
bartbedfd232009-03-26 19:07:15 +000063 return DRD_(s_check_stack_accesses);
bart09dc13f2009-02-14 15:13:31 +000064}
65
66void DRD_(set_check_stack_accesses)(const Bool c)
67{
bartbedfd232009-03-26 19:07:15 +000068 tl_assert(c == False || c == True);
69 DRD_(s_check_stack_accesses) = c;
bart09dc13f2009-02-14 15:13:31 +000070}
71
bart1335ecc2009-02-14 16:10:53 +000072void DRD_(trace_mem_access)(const Addr addr, const SizeT size,
bartbedfd232009-03-26 19:07:15 +000073 const BmAccessTypeT access_type)
bart09dc13f2009-02-14 15:13:31 +000074{
bartbedfd232009-03-26 19:07:15 +000075 if (DRD_(is_any_traced)(addr, addr + size))
76 {
77 char vc[80];
78 DRD_(vc_snprint)(vc, sizeof(vc),
79 DRD_(thread_get_vc)(DRD_(thread_get_running_tid)()));
80 VG_(message)(Vg_UserMsg,
81 "%s 0x%lx size %ld (vg %d / drd %d / vc %s)",
82 access_type == eLoad
83 ? "load "
84 : access_type == eStore
85 ? "store"
86 : access_type == eStart
87 ? "start"
88 : access_type == eEnd
89 ? "end "
90 : "????",
91 addr,
92 size,
93 VG_(get_running_tid)(),
94 DRD_(thread_get_running_tid)(),
95 vc);
96 VG_(get_and_pp_StackTrace)(VG_(get_running_tid)(),
97 VG_(clo_backtrace_size));
98 tl_assert(DRD_(DrdThreadIdToVgThreadId)(DRD_(thread_get_running_tid)())
99 == VG_(get_running_tid)());
100 }
bart09dc13f2009-02-14 15:13:31 +0000101}
102
103static VG_REGPARM(2) void drd_trace_mem_load(const Addr addr, const SizeT size)
104{
bartbedfd232009-03-26 19:07:15 +0000105 return DRD_(trace_mem_access)(addr, size, eLoad);
bart09dc13f2009-02-14 15:13:31 +0000106}
107
108static VG_REGPARM(2) void drd_trace_mem_store(const Addr addr,const SizeT size)
109{
bartbedfd232009-03-26 19:07:15 +0000110 return DRD_(trace_mem_access)(addr, size, eStore);
bart09dc13f2009-02-14 15:13:31 +0000111}
112
113static void drd_report_race(const Addr addr, const SizeT size,
114 const BmAccessTypeT access_type)
115{
bartbedfd232009-03-26 19:07:15 +0000116 DataRaceErrInfo drei;
bart09dc13f2009-02-14 15:13:31 +0000117
bartbedfd232009-03-26 19:07:15 +0000118 drei.tid = DRD_(thread_get_running_tid)();
119 drei.addr = addr;
120 drei.size = size;
121 drei.access_type = access_type;
122 VG_(maybe_record_error)(VG_(get_running_tid)(),
123 DataRaceErr,
124 VG_(get_IP)(VG_(get_running_tid)()),
125 "Conflicting accesses",
126 &drei);
bart09dc13f2009-02-14 15:13:31 +0000127}
128
bart99edb292009-02-15 15:59:20 +0000129VG_REGPARM(2) void DRD_(trace_load)(Addr addr, SizeT size)
bart09dc13f2009-02-14 15:13:31 +0000130{
131#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
bartbedfd232009-03-26 19:07:15 +0000132 /* The assert below has been commented out because of performance reasons.*/
133 tl_assert(thread_get_running_tid()
134 == VgThreadIdToDrdThreadId(VG_(get_running_tid())));
bart09dc13f2009-02-14 15:13:31 +0000135#endif
136
bartbedfd232009-03-26 19:07:15 +0000137 if (DRD_(running_thread_is_recording)()
138 && (DRD_(s_check_stack_accesses)
139 || ! DRD_(thread_address_on_stack)(addr))
140 && bm_access_load_triggers_conflict(addr, addr + size)
141 && ! DRD_(is_suppressed)(addr, addr + size))
142 {
143 drd_report_race(addr, size, eLoad);
144 }
bart09dc13f2009-02-14 15:13:31 +0000145}
146
147static VG_REGPARM(1) void drd_trace_load_1(Addr addr)
148{
bartbedfd232009-03-26 19:07:15 +0000149 if (DRD_(running_thread_is_recording)()
150 && (DRD_(s_check_stack_accesses)
151 || ! DRD_(thread_address_on_stack)(addr))
152 && bm_access_load_1_triggers_conflict(addr)
153 && ! DRD_(is_suppressed)(addr, addr + 1))
154 {
155 drd_report_race(addr, 1, eLoad);
156 }
bart09dc13f2009-02-14 15:13:31 +0000157}
158
159static VG_REGPARM(1) void drd_trace_load_2(Addr addr)
160{
bartbedfd232009-03-26 19:07:15 +0000161 if (DRD_(running_thread_is_recording)()
162 && (DRD_(s_check_stack_accesses)
163 || ! DRD_(thread_address_on_stack)(addr))
164 && bm_access_load_2_triggers_conflict(addr)
165 && ! DRD_(is_suppressed)(addr, addr + 2))
166 {
167 drd_report_race(addr, 2, eLoad);
168 }
bart09dc13f2009-02-14 15:13:31 +0000169}
170
171static VG_REGPARM(1) void drd_trace_load_4(Addr addr)
172{
bartbedfd232009-03-26 19:07:15 +0000173 if (DRD_(running_thread_is_recording)()
174 && (DRD_(s_check_stack_accesses)
175 || ! DRD_(thread_address_on_stack)(addr))
176 && bm_access_load_4_triggers_conflict(addr)
177 && ! DRD_(is_suppressed)(addr, addr + 4))
178 {
179 drd_report_race(addr, 4, eLoad);
180 }
bart09dc13f2009-02-14 15:13:31 +0000181}
182
183static VG_REGPARM(1) void drd_trace_load_8(Addr addr)
184{
bartbedfd232009-03-26 19:07:15 +0000185 if (DRD_(running_thread_is_recording)()
186 && (DRD_(s_check_stack_accesses)
187 || ! DRD_(thread_address_on_stack)(addr))
188 && bm_access_load_8_triggers_conflict(addr)
189 && ! DRD_(is_suppressed)(addr, addr + 8))
190 {
191 drd_report_race(addr, 8, eLoad);
192 }
bart09dc13f2009-02-14 15:13:31 +0000193}
194
bart99edb292009-02-15 15:59:20 +0000195VG_REGPARM(2) void DRD_(trace_store)(Addr addr, SizeT size)
bart09dc13f2009-02-14 15:13:31 +0000196{
197#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
bartbedfd232009-03-26 19:07:15 +0000198 /* The assert below has been commented out because of performance reasons.*/
199 tl_assert(thread_get_running_tid()
200 == VgThreadIdToDrdThreadId(VG_(get_running_tid())));
bart09dc13f2009-02-14 15:13:31 +0000201#endif
202
bartbedfd232009-03-26 19:07:15 +0000203 if (DRD_(running_thread_is_recording)()
204 && (DRD_(s_check_stack_accesses)
205 || ! DRD_(thread_address_on_stack)(addr))
206 && bm_access_store_triggers_conflict(addr, addr + size)
207 && ! DRD_(is_suppressed)(addr, addr + size))
208 {
209 drd_report_race(addr, size, eStore);
210 }
bart09dc13f2009-02-14 15:13:31 +0000211}
212
213static VG_REGPARM(1) void drd_trace_store_1(Addr addr)
214{
bartbedfd232009-03-26 19:07:15 +0000215 if (DRD_(running_thread_is_recording)()
216 && (DRD_(s_check_stack_accesses)
217 || ! DRD_(thread_address_on_stack)(addr))
218 && bm_access_store_1_triggers_conflict(addr)
219 && ! DRD_(is_suppressed)(addr, addr + 1))
220 {
221 drd_report_race(addr, 1, eStore);
222 }
bart09dc13f2009-02-14 15:13:31 +0000223}
224
225static VG_REGPARM(1) void drd_trace_store_2(Addr addr)
226{
bartbedfd232009-03-26 19:07:15 +0000227 if (DRD_(running_thread_is_recording)()
228 && (DRD_(s_check_stack_accesses)
229 || ! DRD_(thread_address_on_stack)(addr))
230 && bm_access_store_2_triggers_conflict(addr)
231 && ! DRD_(is_suppressed)(addr, addr + 2))
232 {
233 drd_report_race(addr, 2, eStore);
234 }
bart09dc13f2009-02-14 15:13:31 +0000235}
236
237static VG_REGPARM(1) void drd_trace_store_4(Addr addr)
238{
bartbedfd232009-03-26 19:07:15 +0000239 if (DRD_(running_thread_is_recording)()
240 && (DRD_(s_check_stack_accesses)
241 || ! DRD_(thread_address_on_stack)(addr))
242 && bm_access_store_4_triggers_conflict(addr)
243 && ! DRD_(is_suppressed)(addr, addr + 4))
244 {
245 drd_report_race(addr, 4, eStore);
246 }
bart09dc13f2009-02-14 15:13:31 +0000247}
248
249static VG_REGPARM(1) void drd_trace_store_8(Addr addr)
250{
bartbedfd232009-03-26 19:07:15 +0000251 if (DRD_(running_thread_is_recording)()
252 && (DRD_(s_check_stack_accesses)
253 || ! DRD_(thread_address_on_stack)(addr))
254 && bm_access_store_8_triggers_conflict(addr)
255 && ! DRD_(is_suppressed)(addr, addr + 8))
256 {
257 drd_report_race(addr, 8, eStore);
258 }
bart09dc13f2009-02-14 15:13:31 +0000259}
260
261/**
262 * Return true if and only if addr_expr matches the pattern (SP) or
263 * <offset>(SP).
264 */
265static Bool is_stack_access(IRSB* const bb, IRExpr* const addr_expr)
266{
bartbedfd232009-03-26 19:07:15 +0000267 Bool result = False;
bart09dc13f2009-02-14 15:13:31 +0000268
bartbedfd232009-03-26 19:07:15 +0000269 if (addr_expr->tag == Iex_RdTmp)
270 {
271 int i;
272 for (i = 0; i < bb->stmts_size; i++)
bart09dc13f2009-02-14 15:13:31 +0000273 {
bartbedfd232009-03-26 19:07:15 +0000274 if (bb->stmts[i]
275 && bb->stmts[i]->tag == Ist_WrTmp
276 && bb->stmts[i]->Ist.WrTmp.tmp == addr_expr->Iex.RdTmp.tmp)
277 {
278 IRExpr* e = bb->stmts[i]->Ist.WrTmp.data;
279 if (e->tag == Iex_Get && e->Iex.Get.offset == STACK_POINTER_OFFSET)
280 {
281 result = True;
282 }
bart09dc13f2009-02-14 15:13:31 +0000283
bartbedfd232009-03-26 19:07:15 +0000284 //ppIRExpr(e);
285 //VG_(printf)(" (%s)\n", result ? "True" : "False");
286 break;
287 }
bart09dc13f2009-02-14 15:13:31 +0000288 }
bartbedfd232009-03-26 19:07:15 +0000289 }
290 return result;
bart09dc13f2009-02-14 15:13:31 +0000291}
292
293static void instrument_load(IRSB* const bb,
294 IRExpr* const addr_expr,
295 const HWord size)
296{
bartbedfd232009-03-26 19:07:15 +0000297 IRExpr* size_expr;
298 IRExpr** argv;
299 IRDirty* di;
bart09dc13f2009-02-14 15:13:31 +0000300
bartbedfd232009-03-26 19:07:15 +0000301 if (UNLIKELY(DRD_(any_address_is_traced)()))
302 {
303 addStmtToIRSB(bb,
304 IRStmt_Dirty(
305 unsafeIRDirty_0_N(/*regparms*/2,
306 "drd_trace_load",
307 VG_(fnptr_to_fnentry)
308 (drd_trace_mem_load),
309 mkIRExprVec_2(addr_expr,
310 mkIRExpr_HWord(size)))));
311 }
bart09dc13f2009-02-14 15:13:31 +0000312
bartbedfd232009-03-26 19:07:15 +0000313 if (! DRD_(s_check_stack_accesses) && is_stack_access(bb, addr_expr))
314 return;
bart09dc13f2009-02-14 15:13:31 +0000315
bartbedfd232009-03-26 19:07:15 +0000316 switch (size)
317 {
318 case 1:
319 argv = mkIRExprVec_1(addr_expr);
320 di = unsafeIRDirty_0_N(/*regparms*/1,
321 "drd_trace_load_1",
322 VG_(fnptr_to_fnentry)(drd_trace_load_1),
323 argv);
324 break;
325 case 2:
326 argv = mkIRExprVec_1(addr_expr);
327 di = unsafeIRDirty_0_N(/*regparms*/1,
328 "drd_trace_load_2",
329 VG_(fnptr_to_fnentry)(drd_trace_load_2),
330 argv);
331 break;
332 case 4:
333 argv = mkIRExprVec_1(addr_expr);
334 di = unsafeIRDirty_0_N(/*regparms*/1,
335 "drd_trace_load_4",
336 VG_(fnptr_to_fnentry)(drd_trace_load_4),
337 argv);
338 break;
339 case 8:
340 argv = mkIRExprVec_1(addr_expr);
341 di = unsafeIRDirty_0_N(/*regparms*/1,
342 "drd_trace_load_8",
343 VG_(fnptr_to_fnentry)(drd_trace_load_8),
344 argv);
345 break;
346 default:
347 size_expr = mkIRExpr_HWord(size);
348 argv = mkIRExprVec_2(addr_expr, size_expr);
349 di = unsafeIRDirty_0_N(/*regparms*/2,
350 "drd_trace_load",
351 VG_(fnptr_to_fnentry)(DRD_(trace_load)),
352 argv);
353 break;
354 }
355 addStmtToIRSB(bb, IRStmt_Dirty(di));
bart09dc13f2009-02-14 15:13:31 +0000356}
357
358static void instrument_store(IRSB* const bb,
359 IRExpr* const addr_expr,
360 const HWord size)
361{
bartbedfd232009-03-26 19:07:15 +0000362 IRExpr* size_expr;
363 IRExpr** argv;
364 IRDirty* di;
bart09dc13f2009-02-14 15:13:31 +0000365
bartbedfd232009-03-26 19:07:15 +0000366 if (UNLIKELY(DRD_(any_address_is_traced)()))
367 {
368 addStmtToIRSB(bb,
369 IRStmt_Dirty(
370 unsafeIRDirty_0_N(/*regparms*/2,
371 "drd_trace_store",
372 VG_(fnptr_to_fnentry)
373 (drd_trace_mem_store),
374 mkIRExprVec_2(addr_expr,
375 mkIRExpr_HWord(size)))));
376 }
bart09dc13f2009-02-14 15:13:31 +0000377
bartbedfd232009-03-26 19:07:15 +0000378 if (! DRD_(s_check_stack_accesses) && is_stack_access(bb, addr_expr))
379 return;
bart09dc13f2009-02-14 15:13:31 +0000380
bartbedfd232009-03-26 19:07:15 +0000381 switch (size)
382 {
383 case 1:
384 argv = mkIRExprVec_1(addr_expr);
385 di = unsafeIRDirty_0_N(/*regparms*/1,
386 "drd_trace_store_1",
387 VG_(fnptr_to_fnentry)(drd_trace_store_1),
388 argv);
389 break;
390 case 2:
391 argv = mkIRExprVec_1(addr_expr);
392 di = unsafeIRDirty_0_N(/*regparms*/1,
393 "drd_trace_store_2",
394 VG_(fnptr_to_fnentry)(drd_trace_store_2),
395 argv);
396 break;
397 case 4:
398 argv = mkIRExprVec_1(addr_expr);
399 di = unsafeIRDirty_0_N(/*regparms*/1,
400 "drd_trace_store_4",
401 VG_(fnptr_to_fnentry)(drd_trace_store_4),
402 argv);
403 break;
404 case 8:
405 argv = mkIRExprVec_1(addr_expr);
406 di = unsafeIRDirty_0_N(/*regparms*/1,
407 "drd_trace_store_8",
408 VG_(fnptr_to_fnentry)(drd_trace_store_8),
409 argv);
410 break;
411 default:
412 size_expr = mkIRExpr_HWord(size);
413 argv = mkIRExprVec_2(addr_expr, size_expr);
414 di = unsafeIRDirty_0_N(/*regparms*/2,
415 "drd_trace_store",
416 VG_(fnptr_to_fnentry)(DRD_(trace_store)),
417 argv);
418 break;
419 }
420 addStmtToIRSB(bb, IRStmt_Dirty(di));
bart09dc13f2009-02-14 15:13:31 +0000421}
422
bart1335ecc2009-02-14 16:10:53 +0000423IRSB* DRD_(instrument)(VgCallbackClosure* const closure,
bartbedfd232009-03-26 19:07:15 +0000424 IRSB* const bb_in,
425 VexGuestLayout* const layout,
426 VexGuestExtents* const vge,
427 IRType const gWordTy,
428 IRType const hWordTy)
bart09dc13f2009-02-14 15:13:31 +0000429{
bartbedfd232009-03-26 19:07:15 +0000430 IRDirty* di;
431 Int i;
432 IRSB* bb;
433 IRExpr** argv;
434 Bool instrument = True;
435 Bool bus_locked = False;
bart09dc13f2009-02-14 15:13:31 +0000436
bartbedfd232009-03-26 19:07:15 +0000437 /* Set up BB */
438 bb = emptyIRSB();
439 bb->tyenv = deepCopyIRTypeEnv(bb_in->tyenv);
440 bb->next = deepCopyIRExpr(bb_in->next);
441 bb->jumpkind = bb_in->jumpkind;
bart09dc13f2009-02-14 15:13:31 +0000442
bartbedfd232009-03-26 19:07:15 +0000443 for (i = 0; i < bb_in->stmts_used; i++)
444 {
445 IRStmt* const st = bb_in->stmts[i];
446 tl_assert(st);
447 if (st->tag == Ist_NoOp)
448 continue;
bart09dc13f2009-02-14 15:13:31 +0000449
bartbedfd232009-03-26 19:07:15 +0000450 switch (st->tag)
bart09dc13f2009-02-14 15:13:31 +0000451 {
bartbedfd232009-03-26 19:07:15 +0000452 /* Note: the code for not instrumenting the code in .plt */
453 /* sections is only necessary on CentOS 3.0 x86 (kernel 2.4.21 */
454 /* + glibc 2.3.2 + NPTL 0.60 + binutils 2.14.90.0.4). */
455 /* This is because on this platform dynamic library symbols are */
456 /* relocated in another way than by later binutils versions. The */
457 /* linker e.g. does not generate .got.plt sections on CentOS 3.0. */
458 case Ist_IMark:
459 instrument = VG_(seginfo_sect_kind)(NULL, 0, st->Ist.IMark.addr)
460 != Vg_SectPLT;
461 addStmtToIRSB(bb, st);
462 break;
463
464 case Ist_MBE:
465 switch (st->Ist.MBE.event)
466 {
467 case Imbe_Fence:
468 break; /* not interesting */
469 case Imbe_BusLock:
470 case Imbe_SnoopedStoreBegin:
471 tl_assert(! bus_locked);
472 bus_locked = True;
473 break;
474 case Imbe_BusUnlock:
475 case Imbe_SnoopedStoreEnd:
476 tl_assert(bus_locked);
477 bus_locked = False;
478 break;
479 default:
480 tl_assert(0);
481 }
482 addStmtToIRSB(bb, st);
483 break;
484
485 case Ist_Store:
486 if (instrument && ! bus_locked)
487 {
488 instrument_store(bb,
489 st->Ist.Store.addr,
490 sizeofIRType(typeOfIRExpr(bb->tyenv,
491 st->Ist.Store.data)));
492 }
493 addStmtToIRSB(bb, st);
494 break;
495
496 case Ist_WrTmp:
497 if (instrument)
498 {
499 const IRExpr* const data = st->Ist.WrTmp.data;
500 if (data->tag == Iex_Load)
501 {
502 instrument_load(bb,
503 data->Iex.Load.addr,
504 sizeofIRType(data->Iex.Load.ty));
505 }
506 }
507 addStmtToIRSB(bb, st);
508 break;
509
510 case Ist_Dirty:
511 if (instrument)
512 {
513 IRDirty* d = st->Ist.Dirty.details;
514 IREffect const mFx = d->mFx;
515 switch (mFx) {
516 case Ifx_None:
517 break;
518 case Ifx_Read:
519 case Ifx_Write:
520 case Ifx_Modify:
521 tl_assert(d->mAddr);
522 tl_assert(d->mSize > 0);
523 argv = mkIRExprVec_2(d->mAddr, mkIRExpr_HWord(d->mSize));
524 if (mFx == Ifx_Read || mFx == Ifx_Modify) {
525 di = unsafeIRDirty_0_N(
526 /*regparms*/2,
527 "drd_trace_load",
528 VG_(fnptr_to_fnentry)(DRD_(trace_load)),
529 argv);
530 addStmtToIRSB(bb, IRStmt_Dirty(di));
531 }
532 if ((mFx == Ifx_Write || mFx == Ifx_Modify)
533 && ! bus_locked)
534 {
535 di = unsafeIRDirty_0_N(
536 /*regparms*/2,
537 "drd_trace_store",
538 VG_(fnptr_to_fnentry)(DRD_(trace_store)),
539 argv);
540 addStmtToIRSB(bb, IRStmt_Dirty(di));
541 }
542 break;
543 default:
544 tl_assert(0);
545 }
546 }
547 addStmtToIRSB(bb, st);
548 break;
549
bart09dc13f2009-02-14 15:13:31 +0000550 default:
bartbedfd232009-03-26 19:07:15 +0000551 addStmtToIRSB(bb, st);
552 break;
bart09dc13f2009-02-14 15:13:31 +0000553 }
bartbedfd232009-03-26 19:07:15 +0000554 }
bart09dc13f2009-02-14 15:13:31 +0000555
bartbedfd232009-03-26 19:07:15 +0000556 tl_assert(! bus_locked);
bart09dc13f2009-02-14 15:13:31 +0000557
bartbedfd232009-03-26 19:07:15 +0000558 return bb;
bart09dc13f2009-02-14 15:13:31 +0000559}
560