1/*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "BreakpointManager.h"
7
8#include <algorithm>
9
10#include <AutoDeleter.h>
11
12#include <kernel.h>
13#include <util/AutoLock.h>
14#include <vm/vm.h>
15
16
17//#define TRACE_BREAKPOINT_MANAGER
18#ifdef TRACE_BREAKPOINT_MANAGER
19#	define TRACE(x...) dprintf(x)
20#else
21#	define TRACE(x...) do {} while (false)
22#endif
23
24
25// soft limit for the number of breakpoints
26const int32 kMaxBreakpointCount = 10240;
27
28
29BreakpointManager::InstalledBreakpoint::InstalledBreakpoint(addr_t address)
30	:
31	breakpoint(NULL),
32	address(address)
33{
34}
35
36
37// #pragma mark -
38
39
40BreakpointManager::BreakpointManager()
41	:
42	fBreakpointCount(0),
43	fWatchpointCount(0)
44{
45	rw_lock_init(&fLock, "breakpoint manager");
46}
47
48
49BreakpointManager::~BreakpointManager()
50{
51	WriteLocker locker(fLock);
52
53	// delete the installed breakpoint objects
54	BreakpointTree::Iterator it = fBreakpoints.GetIterator();
55	while (InstalledBreakpoint* installedBreakpoint = it.Next()) {
56		it.Remove();
57
58		// delete underlying software breakpoint
59		if (installedBreakpoint->breakpoint->software)
60			delete installedBreakpoint->breakpoint;
61
62		delete installedBreakpoint;
63	}
64
65	// delete the watchpoints
66	while (InstalledWatchpoint* watchpoint = fWatchpoints.RemoveHead())
67		delete watchpoint;
68
69	// delete the hardware breakpoint objects
70	while (Breakpoint* breakpoint = fHardwareBreakpoints.RemoveHead())
71		delete breakpoint;
72
73	rw_lock_destroy(&fLock);
74}
75
76
77status_t
78BreakpointManager::Init()
79{
80	// create objects for the hardware breakpoints
81	for (int32 i = 0; i < DEBUG_MAX_BREAKPOINTS; i++) {
82		Breakpoint* breakpoint = new(std::nothrow) Breakpoint;
83		if (breakpoint == NULL)
84			return B_NO_MEMORY;
85
86		breakpoint->address = 0;
87		breakpoint->installedBreakpoint = NULL;
88		breakpoint->used = false;
89		breakpoint->software = false;
90
91		fHardwareBreakpoints.Add(breakpoint);
92	}
93
94	return B_OK;
95}
96
97
98status_t
99BreakpointManager::InstallBreakpoint(void* _address)
100{
101	const addr_t address = (addr_t)_address;
102
103	WriteLocker locker(fLock);
104
105	if (fBreakpointCount >= kMaxBreakpointCount)
106		return B_BUSY;
107
108	// check whether there's already a breakpoint at the address
109	InstalledBreakpoint* installed = fBreakpoints.Lookup(address);
110	if (installed != NULL)
111		return B_BAD_VALUE;
112
113	// create the breakpoint object
114	installed = new(std::nothrow) InstalledBreakpoint(address);
115	if (installed == NULL)
116		return B_NO_MEMORY;
117	ObjectDeleter<InstalledBreakpoint> installedDeleter(installed);
118
119	// If we still have enough hardware breakpoints left, install a hardware
120	// breakpoint.
121	Breakpoint* breakpoint = _GetUnusedHardwareBreakpoint(false);
122	if (breakpoint != NULL) {
123		status_t error = _InstallHardwareBreakpoint(breakpoint, address);
124		if (error != B_OK)
125			return error;
126
127		breakpoint->installedBreakpoint = installed;
128		installed->breakpoint = breakpoint;
129	} else {
130		// install a software breakpoint
131		status_t error = _InstallSoftwareBreakpoint(installed, address);
132		if (error != B_OK)
133			return error;
134	}
135
136	fBreakpoints.Insert(installed);
137	installedDeleter.Detach();
138	fBreakpointCount++;
139
140	return B_OK;
141}
142
143
144status_t
145BreakpointManager::UninstallBreakpoint(void* _address)
146{
147	const addr_t address = (addr_t)_address;
148
149	WriteLocker locker(fLock);
150
151	InstalledBreakpoint* installed = fBreakpoints.Lookup(address);
152	if (installed == NULL)
153		return B_BAD_VALUE;
154
155	if (installed->breakpoint->software)
156		_UninstallSoftwareBreakpoint(installed->breakpoint);
157	else
158		_UninstallHardwareBreakpoint(installed->breakpoint);
159
160	fBreakpoints.Remove(installed);
161	delete installed;
162	fBreakpointCount--;
163
164	return B_OK;
165}
166
167
168status_t
169BreakpointManager::InstallWatchpoint(void* _address, uint32 type, int32 length)
170{
171	const addr_t address = (addr_t)_address;
172
173	WriteLocker locker(fLock);
174
175	InstalledWatchpoint* watchpoint = _FindWatchpoint(address);
176	if (watchpoint != NULL)
177		return B_BAD_VALUE;
178
179#if DEBUG_SHARED_BREAK_AND_WATCHPOINTS
180	// We need at least one hardware breakpoint for our breakpoint management.
181	if (fWatchpointCount + 1 >= DEBUG_MAX_WATCHPOINTS)
182		return B_BUSY;
183#else
184	if (fWatchpointCount >= DEBUG_MAX_WATCHPOINTS)
185		return B_BUSY;
186#endif
187
188	watchpoint = new(std::nothrow) InstalledWatchpoint;
189	if (watchpoint == NULL)
190		return B_NO_MEMORY;
191	ObjectDeleter<InstalledWatchpoint> watchpointDeleter(watchpoint);
192
193	status_t error = _InstallWatchpoint(watchpoint, address, type, length);
194	if (error != B_OK)
195		return error;
196
197	fWatchpoints.Add(watchpointDeleter.Detach());
198	fWatchpointCount++;
199	return B_OK;
200}
201
202
203status_t
204BreakpointManager::UninstallWatchpoint(void* address)
205{
206	WriteLocker locker(fLock);
207
208	InstalledWatchpoint* watchpoint = _FindWatchpoint((addr_t)address);
209	if (watchpoint == NULL)
210		return B_BAD_VALUE;
211
212	ObjectDeleter<InstalledWatchpoint> deleter(watchpoint);
213	fWatchpoints.Remove(watchpoint);
214	fWatchpointCount--;
215
216	return _UninstallWatchpoint(watchpoint);
217}
218
219
220void
221BreakpointManager::RemoveAllBreakpoints()
222{
223	WriteLocker locker(fLock);
224
225	// remove the breakpoints
226	BreakpointTree::Iterator it = fBreakpoints.GetIterator();
227	while (InstalledBreakpoint* installedBreakpoint = it.Next()) {
228		it.Remove();
229
230		// uninstall underlying hard/software breakpoint
231		if (installedBreakpoint->breakpoint->software)
232			_UninstallSoftwareBreakpoint(installedBreakpoint->breakpoint);
233		else
234			_UninstallHardwareBreakpoint(installedBreakpoint->breakpoint);
235
236		delete installedBreakpoint;
237	}
238
239	// remove the watchpoints
240	while (InstalledWatchpoint* watchpoint = fWatchpoints.RemoveHead()) {
241		_UninstallWatchpoint(watchpoint);
242		delete watchpoint;
243	}
244}
245
246
247/*!	\brief Returns whether the given address can be accessed in principle.
248	No check whether there's an actually accessible area is performed, though.
249*/
250/*static*/ bool
251BreakpointManager::CanAccessAddress(const void* _address, bool write)
252{
253	const addr_t address = (addr_t)_address;
254
255	// user addresses are always fine
256	if (IS_USER_ADDRESS(address))
257		return true;
258
259	return false;
260}
261
262
263/*!	\brief Reads data from user memory.
264
265	Tries to read \a size bytes of data from user memory address \a address
266	into the supplied buffer \a buffer. If only a part could be read the
267	function won't fail. The number of bytes actually read is return through
268	\a bytesRead.
269
270	\param address The user memory address from which to read.
271	\param buffer The buffer into which to write.
272	\param size The number of bytes to read.
273	\param bytesRead Will be set to the number of bytes actually read.
274	\return \c B_OK, if reading went fine. Then \a bytesRead will be set to
275			the amount of data actually read. An error indicates that nothing
276			has been read.
277*/
278status_t
279BreakpointManager::ReadMemory(const void* _address, void* buffer, size_t size,
280	size_t& bytesRead)
281{
282	const addr_t address = (addr_t)_address;
283
284	ReadLocker locker(fLock);
285
286	status_t error = _ReadMemory(address, buffer, size, bytesRead);
287	if (error != B_OK)
288		return error;
289
290	// If we have software breakpoints installed, fix the buffer not to contain
291	// any of them.
292
293	// address of the first possibly intersecting software breakpoint
294	const addr_t startAddress
295		= std::max(address, (addr_t)DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1)
296			- (DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1);
297
298	BreakpointTree::Iterator it = fBreakpoints.GetIterator(startAddress, true,
299		true);
300	while (InstalledBreakpoint* installed = it.Next()) {
301		Breakpoint* breakpoint = installed->breakpoint;
302		if (breakpoint->address >= address + size)
303			break;
304
305		if (breakpoint->software) {
306			// Software breakpoint intersects -- replace the read data with
307			// the data saved in the breakpoint object.
308			addr_t minAddress = std::max(breakpoint->address, address);
309			size_t toCopy = std::min(address + size,
310					breakpoint->address + DEBUG_SOFTWARE_BREAKPOINT_SIZE)
311				- minAddress;
312			memcpy((uint8*)buffer + (minAddress - address),
313				breakpoint->softwareData + (minAddress - breakpoint->address),
314				toCopy);
315		}
316	}
317
318	return B_OK;
319}
320
321
322status_t
323BreakpointManager::WriteMemory(void* _address, const void* _buffer, size_t size,
324	size_t& bytesWritten)
325{
326	bytesWritten = 0;
327
328	if (size == 0)
329		return B_OK;
330
331	addr_t address = (addr_t)_address;
332	const uint8* buffer = (uint8*)_buffer;
333
334	WriteLocker locker(fLock);
335
336	// We don't want to overwrite software breakpoints, so things are a bit more
337	// complicated. We iterate through the intersecting software breakpoints,
338	// writing the memory between them normally, but skipping the breakpoints
339	// itself. We write into their softwareData instead.
340
341	// Get the first breakpoint -- if it starts before the address, we'll
342	// handle it separately to make things in the main loop simpler.
343	const addr_t startAddress
344		= std::max(address, (addr_t)DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1)
345			- (DEBUG_SOFTWARE_BREAKPOINT_SIZE - 1);
346
347	BreakpointTree::Iterator it = fBreakpoints.GetIterator(startAddress, true,
348		true);
349	InstalledBreakpoint* installed = it.Next();
350	while (installed != NULL) {
351		Breakpoint* breakpoint = installed->breakpoint;
352		if (breakpoint->address >= address)
353			break;
354
355		if (breakpoint->software) {
356			// We've got a breakpoint that is partially intersecting with the
357			// beginning of the address range to write.
358			size_t toCopy = std::min(address + size,
359					breakpoint->address + DEBUG_SOFTWARE_BREAKPOINT_SIZE)
360				- address;
361			memcpy(breakpoint->softwareData + (address - breakpoint->address),
362				buffer, toCopy);
363
364			address += toCopy;
365			size -= toCopy;
366			bytesWritten += toCopy;
367			buffer += toCopy;
368		}
369
370		installed = it.Next();
371	}
372
373	// loop through the breakpoints intersecting with the range
374	while (installed != NULL) {
375		Breakpoint* breakpoint = installed->breakpoint;
376		if (breakpoint->address >= address + size)
377			break;
378
379		if (breakpoint->software) {
380			// write the data up to the breakpoint (if any)
381			size_t toCopy = breakpoint->address - address;
382			if (toCopy > 0) {
383				size_t chunkWritten;
384				status_t error = _WriteMemory(address, buffer, toCopy,
385					chunkWritten);
386				if (error != B_OK)
387					return bytesWritten > 0 ? B_OK : error;
388
389				address += chunkWritten;
390				size -= chunkWritten;
391				bytesWritten += chunkWritten;
392				buffer += chunkWritten;
393
394				if (chunkWritten < toCopy)
395					return B_OK;
396			}
397
398			// write to the breakpoint data
399			toCopy = std::min(size, (size_t)DEBUG_SOFTWARE_BREAKPOINT_SIZE);
400			memcpy(breakpoint->softwareData, buffer, toCopy);
401
402			address += toCopy;
403			size -= toCopy;
404			bytesWritten += toCopy;
405			buffer += toCopy;
406		}
407
408		installed = it.Next();
409	}
410
411	// write remaining data
412	if (size > 0) {
413		size_t chunkWritten;
414		status_t error = _WriteMemory(address, buffer, size, chunkWritten);
415		if (error != B_OK)
416			return bytesWritten > 0 ? B_OK : error;
417
418		bytesWritten += chunkWritten;
419	}
420
421	return B_OK;
422}
423
424
425void
426BreakpointManager::PrepareToContinue(void* _address)
427{
428	const addr_t address = (addr_t)_address;
429
430	WriteLocker locker(fLock);
431
432	// Check whether there's a software breakpoint at the continuation address.
433	InstalledBreakpoint* installed = fBreakpoints.Lookup(address);
434	if (installed == NULL || !installed->breakpoint->software)
435		return;
436
437	// We need to replace the software breakpoint by a hardware one, or
438	// we can't continue the thread.
439	Breakpoint* breakpoint = _GetUnusedHardwareBreakpoint(true);
440	if (breakpoint == NULL) {
441		dprintf("Failed to allocate a hardware breakpoint.\n");
442		return;
443	}
444
445	status_t error = _InstallHardwareBreakpoint(breakpoint, address);
446	if (error != B_OK)
447		return;
448
449	_UninstallSoftwareBreakpoint(installed->breakpoint);
450
451	breakpoint->installedBreakpoint = installed;
452	installed->breakpoint = breakpoint;
453}
454
455
456BreakpointManager::Breakpoint*
457BreakpointManager::_GetUnusedHardwareBreakpoint(bool force)
458{
459	// try to find a free one first
460	for (BreakpointList::Iterator it = fHardwareBreakpoints.GetIterator();
461			Breakpoint* breakpoint = it.Next();) {
462		if (!breakpoint->used)
463			return breakpoint;
464	}
465
466	if (!force)
467		return NULL;
468
469	// replace one by a software breakpoint
470	for (BreakpointList::Iterator it = fHardwareBreakpoints.GetIterator();
471			Breakpoint* breakpoint = it.Next();) {
472		if (breakpoint->installedBreakpoint == NULL)
473			continue;
474
475		status_t error = _InstallSoftwareBreakpoint(
476			breakpoint->installedBreakpoint, breakpoint->address);
477		if (error != B_OK)
478			continue;
479
480		if (_UninstallHardwareBreakpoint(breakpoint) == B_OK)
481			return breakpoint;
482	}
483
484	return NULL;
485}
486
487
488status_t
489BreakpointManager::_InstallSoftwareBreakpoint(InstalledBreakpoint* installed,
490	addr_t address)
491{
492	Breakpoint* breakpoint = new(std::nothrow) Breakpoint;
493	if (breakpoint == NULL)
494		return B_NO_MEMORY;
495	ObjectDeleter<Breakpoint> breakpointDeleter(breakpoint);
496
497	breakpoint->address = address;
498	breakpoint->installedBreakpoint = installed;
499	breakpoint->used = true;
500	breakpoint->software = true;
501
502	// save the memory where the software breakpoint shall be installed
503	size_t bytesTransferred;
504	status_t error = _ReadMemory(address, breakpoint->softwareData,
505		DEBUG_SOFTWARE_BREAKPOINT_SIZE, bytesTransferred);
506	if (error != B_OK)
507		return error;
508	if (bytesTransferred != DEBUG_SOFTWARE_BREAKPOINT_SIZE)
509		return B_BAD_ADDRESS;
510
511	// write the breakpoint code
512	error = _WriteMemory(address, DEBUG_SOFTWARE_BREAKPOINT,
513		DEBUG_SOFTWARE_BREAKPOINT_SIZE, bytesTransferred);
514	if (error != B_OK)
515		return error;
516
517	if (bytesTransferred < DEBUG_SOFTWARE_BREAKPOINT_SIZE) {
518		// breakpoint written partially only -- undo the written part
519		if (bytesTransferred > 0) {
520			size_t dummy;
521			_WriteMemory(address, breakpoint->softwareData, bytesTransferred,
522				dummy);
523		}
524		return B_BAD_ADDRESS;
525	}
526
527	installed->breakpoint = breakpoint;
528	breakpointDeleter.Detach();
529
530	TRACE("installed software breakpoint at %#lx\n", address);
531
532	return B_OK;
533}
534
535
536status_t
537BreakpointManager::_UninstallSoftwareBreakpoint(Breakpoint* breakpoint)
538{
539	size_t bytesWritten;
540	_WriteMemory(breakpoint->address, breakpoint->softwareData,
541		DEBUG_SOFTWARE_BREAKPOINT_SIZE, bytesWritten);
542
543	TRACE("uninstalled software breakpoint at %#lx\n", breakpoint->address);
544
545	delete breakpoint;
546	return B_OK;
547}
548
549
550status_t
551BreakpointManager::_InstallHardwareBreakpoint(Breakpoint* breakpoint,
552	addr_t address)
553{
554	status_t error = arch_set_breakpoint((void*)address);
555	if (error != B_OK)
556		return error;
557
558	// move to the tail of the list
559	fHardwareBreakpoints.Remove(breakpoint);
560	fHardwareBreakpoints.Add(breakpoint);
561
562	TRACE("installed hardware breakpoint at %#lx\n", address);
563
564	breakpoint->address = address;
565	breakpoint->used = true;
566	return B_OK;
567}
568
569
570status_t
571BreakpointManager::_UninstallHardwareBreakpoint(Breakpoint* breakpoint)
572{
573	status_t error = arch_clear_breakpoint((void*)breakpoint->address);
574	if (error != B_OK)
575		return error;
576
577	TRACE("uninstalled hardware breakpoint at %#lx\n", breakpoint->address);
578
579	breakpoint->used = false;
580	breakpoint->installedBreakpoint = NULL;
581	return B_OK;
582}
583
584
585BreakpointManager::InstalledWatchpoint*
586BreakpointManager::_FindWatchpoint(addr_t address) const
587{
588	for (InstalledWatchpointList::ConstIterator it = fWatchpoints.GetIterator();
589		InstalledWatchpoint* watchpoint = it.Next();) {
590		if (address == watchpoint->address)
591			return watchpoint;
592	}
593
594	return NULL;
595}
596
597
598status_t
599BreakpointManager::_InstallWatchpoint(InstalledWatchpoint* watchpoint,
600	addr_t address, uint32 type, int32 length)
601{
602#if DEBUG_SHARED_BREAK_AND_WATCHPOINTS
603	// We need a hardware breakpoint.
604	watchpoint->breakpoint = _GetUnusedHardwareBreakpoint(true);
605	if (watchpoint->breakpoint == NULL) {
606		dprintf("Failed to allocate a hardware breakpoint for watchpoint.\n");
607		return B_BUSY;
608	}
609#endif
610
611	status_t error = arch_set_watchpoint((void*)address, type, length);
612	if (error != B_OK)
613		return error;
614
615	watchpoint->address = address;
616
617#if DEBUG_SHARED_BREAK_AND_WATCHPOINTS
618	watchpoint->breakpoint->used = true;
619#endif
620
621	return B_OK;
622}
623
624
625status_t
626BreakpointManager::_UninstallWatchpoint(InstalledWatchpoint* watchpoint)
627{
628#if DEBUG_SHARED_BREAK_AND_WATCHPOINTS
629	watchpoint->breakpoint->used = false;
630#endif
631
632	return arch_clear_watchpoint((void*)watchpoint->address);
633}
634
635
636status_t
637BreakpointManager::_ReadMemory(const addr_t _address, void* _buffer,
638	size_t size, size_t& bytesRead)
639{
640	const uint8* address = (const uint8*)_address;
641	uint8* buffer = (uint8*)_buffer;
642
643	// check the parameters
644	if (!CanAccessAddress(address, false))
645		return B_BAD_ADDRESS;
646	if (size <= 0)
647		return B_BAD_VALUE;
648
649	// If the region to be read crosses page boundaries, we split it up into
650	// smaller chunks.
651	status_t error = B_OK;
652	bytesRead = 0;
653	while (size > 0) {
654		// check whether we're still in user address space
655		if (!CanAccessAddress(address, false)) {
656			error = B_BAD_ADDRESS;
657			break;
658		}
659
660		// don't cross page boundaries in a single read
661		int32 toRead = size;
662		int32 maxRead = B_PAGE_SIZE - (addr_t)address % B_PAGE_SIZE;
663		if (toRead > maxRead)
664			toRead = maxRead;
665
666		error = user_memcpy(buffer, address, toRead);
667		if (error != B_OK)
668			break;
669
670		bytesRead += toRead;
671		address += toRead;
672		buffer += toRead;
673		size -= toRead;
674	}
675
676	// If reading fails, we only fail, if we haven't read anything yet.
677	if (error != B_OK) {
678		if (bytesRead > 0)
679			return B_OK;
680		return error;
681	}
682
683	return B_OK;
684}
685
686
687status_t
688BreakpointManager::_WriteMemory(addr_t _address, const void* _buffer,
689	size_t size, size_t& bytesWritten)
690{
691	uint8* address = (uint8*)_address;
692	const uint8* buffer = (const uint8*)_buffer;
693
694	// check the parameters
695	if (!CanAccessAddress(address, true))
696		return B_BAD_ADDRESS;
697	if (size <= 0)
698		return B_BAD_VALUE;
699
700	// If the region to be written crosses area boundaries, we split it up into
701	// smaller chunks.
702	status_t error = B_OK;
703	bytesWritten = 0;
704	while (size > 0) {
705		// check whether we're still in user address space
706		if (!CanAccessAddress(address, true)) {
707			error = B_BAD_ADDRESS;
708			break;
709		}
710
711		// get the area for the address (we need to use _user_area_for(), since
712		// we're looking for a user area)
713		area_id area = _user_area_for(address);
714		if (area < 0) {
715			TRACE("BreakpointManager::_WriteMemory(): area not found for "
716				"address: %p: %lx\n", address, area);
717			error = area;
718			break;
719		}
720
721		area_info areaInfo;
722		status_t error = get_area_info(area, &areaInfo);
723		if (error != B_OK) {
724			TRACE("BreakpointManager::_WriteMemory(): failed to get info for "
725				"area %ld: %lx\n", area, error);
726			error = B_BAD_ADDRESS;
727			break;
728		}
729
730		// restrict this round of writing to the found area
731		int32 toWrite = size;
732		int32 maxWrite = (uint8*)areaInfo.address + areaInfo.size - address;
733		if (toWrite > maxWrite)
734			toWrite = maxWrite;
735
736		// if the area is read-only, we temporarily need to make it writable
737		bool protectionChanged = false;
738		if (!(areaInfo.protection & (B_WRITE_AREA | B_KERNEL_WRITE_AREA))) {
739			error = set_area_protection(area,
740				areaInfo.protection | B_WRITE_AREA);
741			if (error != B_OK) {
742				TRACE("BreakpointManager::_WriteMemory(): failed to set new "
743					"protection for area %ld: %lx\n", area, error);
744				break;
745			}
746			protectionChanged = true;
747		}
748
749		// copy the memory
750		error = user_memcpy(address, buffer, toWrite);
751
752		// reset the area protection
753		if (protectionChanged)
754			set_area_protection(area, areaInfo.protection);
755
756		if (error != B_OK) {
757			TRACE("BreakpointManager::_WriteMemory(): user_memcpy() failed: "
758				"%lx\n", error);
759			break;
760		}
761
762		bytesWritten += toWrite;
763		address += toWrite;
764		buffer += toWrite;
765		size -= toWrite;
766	}
767
768	// If writing fails, we only fail, if we haven't written anything yet.
769	if (error != B_OK) {
770		if (bytesWritten > 0)
771			return B_OK;
772		return error;
773	}
774
775	return B_OK;
776}
777