xref: /haiku/src/system/kernel/debug/BreakpointManager.cpp (revision 25a7b01d15612846f332751841da3579db313082)
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
26 const int32 kMaxBreakpointCount = 10240;
27 
28 
InstalledBreakpoint(addr_t address)29 BreakpointManager::InstalledBreakpoint::InstalledBreakpoint(addr_t address)
30 	:
31 	breakpoint(NULL),
32 	address(address)
33 {
34 }
35 
36 
37 // #pragma mark -
38 
39 
BreakpointManager()40 BreakpointManager::BreakpointManager()
41 	:
42 	fBreakpointCount(0),
43 	fWatchpointCount(0)
44 {
45 	rw_lock_init(&fLock, "breakpoint manager");
46 }
47 
48 
~BreakpointManager()49 BreakpointManager::~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 
77 status_t
Init()78 BreakpointManager::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 
98 status_t
InstallBreakpoint(void * _address)99 BreakpointManager::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 
144 status_t
UninstallBreakpoint(void * _address)145 BreakpointManager::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 
168 status_t
InstallWatchpoint(void * _address,uint32 type,int32 length)169 BreakpointManager::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 
203 status_t
UninstallWatchpoint(void * address)204 BreakpointManager::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 
220 void
RemoveAllBreakpoints()221 BreakpointManager::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
CanAccessAddress(const void * _address,bool write)251 BreakpointManager::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 */
278 status_t
ReadMemory(const void * _address,void * buffer,size_t size,size_t & bytesRead)279 BreakpointManager::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 
322 status_t
WriteMemory(void * _address,const void * _buffer,size_t size,size_t & bytesWritten)323 BreakpointManager::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 
425 void
PrepareToContinue(void * _address)426 BreakpointManager::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 
456 BreakpointManager::Breakpoint*
_GetUnusedHardwareBreakpoint(bool force)457 BreakpointManager::_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 
488 status_t
_InstallSoftwareBreakpoint(InstalledBreakpoint * installed,addr_t address)489 BreakpointManager::_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 
536 status_t
_UninstallSoftwareBreakpoint(Breakpoint * breakpoint)537 BreakpointManager::_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 
550 status_t
_InstallHardwareBreakpoint(Breakpoint * breakpoint,addr_t address)551 BreakpointManager::_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 
570 status_t
_UninstallHardwareBreakpoint(Breakpoint * breakpoint)571 BreakpointManager::_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 
585 BreakpointManager::InstalledWatchpoint*
_FindWatchpoint(addr_t address) const586 BreakpointManager::_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 
598 status_t
_InstallWatchpoint(InstalledWatchpoint * watchpoint,addr_t address,uint32 type,int32 length)599 BreakpointManager::_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 
625 status_t
_UninstallWatchpoint(InstalledWatchpoint * watchpoint)626 BreakpointManager::_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 
636 status_t
_ReadMemory(const addr_t _address,void * _buffer,size_t size,size_t & bytesRead)637 BreakpointManager::_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 
687 status_t
_WriteMemory(addr_t _address,const void * _buffer,size_t size,size_t & bytesWritten)688 BreakpointManager::_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