xref: /onnv-gate/usr/src/uts/intel/io/pci/pci_memlist.c (revision 11474:857f9db4ef05)
13446Smrj /*
23446Smrj  * CDDL HEADER START
33446Smrj  *
43446Smrj  * The contents of this file are subject to the terms of the
56095Sgs150176  * Common Development and Distribution License (the "License").
66095Sgs150176  * You may not use this file except in compliance with the License.
73446Smrj  *
83446Smrj  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93446Smrj  * or http://www.opensolaris.org/os/licensing.
103446Smrj  * See the License for the specific language governing permissions
113446Smrj  * and limitations under the License.
123446Smrj  *
133446Smrj  * When distributing Covered Code, include this CDDL HEADER in each
143446Smrj  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153446Smrj  * If applicable, add the following below this CDDL HEADER, with the
163446Smrj  * fields enclosed by brackets "[]" replaced with your own identifying
173446Smrj  * information: Portions Copyright [yyyy] [name of copyright owner]
183446Smrj  *
193446Smrj  * CDDL HEADER END
203446Smrj  */
213446Smrj /*
22*11474SJonathan.Adams@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
233446Smrj  * Use is subject to license terms.
243446Smrj  */
253446Smrj 
263446Smrj /*
273446Smrj  * XXX This stuff should be in usr/src/common, to be shared by boot
283446Smrj  * code, kernel DR, and busra stuff.
293446Smrj  *
303446Smrj  * NOTE: We are only using the next-> link. The prev-> link is
313446Smrj  *	not used in the implementation.
323446Smrj  */
333446Smrj #include <sys/types.h>
343446Smrj #include <sys/memlist.h>
353446Smrj #include <sys/kmem.h>
363446Smrj #include <sys/cmn_err.h>
373446Smrj #include <sys/pci_impl.h>
383446Smrj #include <sys/debug.h>
393446Smrj 
403446Smrj extern int pci_boot_debug;
413446Smrj #define	dprintf if (pci_boot_debug) printf
423446Smrj 
433446Smrj void
memlist_dump(struct memlist * listp)443446Smrj memlist_dump(struct memlist *listp)
453446Smrj {
463446Smrj 	dprintf("memlist 0x%p content", (void *)listp);
473446Smrj 	while (listp) {
483446Smrj 		dprintf("(0x%x%x, 0x%x%x)",
49*11474SJonathan.Adams@Sun.COM 		    (int)(listp->ml_address >> 32), (int)listp->ml_address,
50*11474SJonathan.Adams@Sun.COM 		    (int)(listp->ml_size >> 32), (int)listp->ml_size);
51*11474SJonathan.Adams@Sun.COM 		listp = listp->ml_next;
523446Smrj 	}
533446Smrj }
543446Smrj 
553446Smrj struct memlist *
memlist_alloc()563446Smrj memlist_alloc()
573446Smrj {
583446Smrj 	return ((struct memlist *)kmem_zalloc(sizeof (struct memlist),
593446Smrj 	    KM_SLEEP));
603446Smrj }
613446Smrj 
623446Smrj void
memlist_free(struct memlist * buf)633446Smrj memlist_free(struct memlist *buf)
643446Smrj {
653446Smrj 	kmem_free(buf, sizeof (struct memlist));
663446Smrj }
673446Smrj 
686095Sgs150176 void
memlist_free_all(struct memlist ** list)696095Sgs150176 memlist_free_all(struct memlist **list)
706095Sgs150176 {
716095Sgs150176 	struct memlist  *next, *buf;
726095Sgs150176 
736095Sgs150176 	next = *list;
746095Sgs150176 	while (next) {
756095Sgs150176 		buf = next;
76*11474SJonathan.Adams@Sun.COM 		next = buf->ml_next;
776095Sgs150176 		kmem_free(buf, sizeof (struct memlist));
786095Sgs150176 	}
796095Sgs150176 	*list = 0;
806095Sgs150176 }
816095Sgs150176 
823446Smrj /* insert in the order of addresses */
833446Smrj void
memlist_insert(struct memlist ** listp,uint64_t addr,uint64_t size)843446Smrj memlist_insert(struct memlist **listp, uint64_t addr, uint64_t size)
853446Smrj {
863446Smrj 	int merge_left, merge_right;
873446Smrj 	struct memlist *entry;
883446Smrj 	struct memlist *prev = 0, *next;
893446Smrj 
903446Smrj 	/* find the location in list */
913446Smrj 	next = *listp;
92*11474SJonathan.Adams@Sun.COM 	while (next && next->ml_address <= addr) {
9310251SDan.Mick@Sun.COM 		/*
9410251SDan.Mick@Sun.COM 		 * Drop if this entry already exists, in whole
9510251SDan.Mick@Sun.COM 		 * or in part
9610251SDan.Mick@Sun.COM 		 */
97*11474SJonathan.Adams@Sun.COM 		if (next->ml_address <= addr &&
98*11474SJonathan.Adams@Sun.COM 		    next->ml_address + next->ml_size >= addr + size) {
9910251SDan.Mick@Sun.COM 			/* next already contains this entire element; drop */
10010251SDan.Mick@Sun.COM 			return;
10110251SDan.Mick@Sun.COM 		}
10210251SDan.Mick@Sun.COM 
10310251SDan.Mick@Sun.COM 		/* Is this a "grow block size" request? */
104*11474SJonathan.Adams@Sun.COM 		if (next->ml_address == addr) {
10510251SDan.Mick@Sun.COM 			break;
10610251SDan.Mick@Sun.COM 		}
1073446Smrj 		prev = next;
108*11474SJonathan.Adams@Sun.COM 		next = prev->ml_next;
1093446Smrj 	}
1103446Smrj 
111*11474SJonathan.Adams@Sun.COM 	merge_left = (prev && addr == prev->ml_address + prev->ml_size);
112*11474SJonathan.Adams@Sun.COM 	merge_right = (next && addr + size == next->ml_address);
1133446Smrj 	if (merge_left && merge_right) {
114*11474SJonathan.Adams@Sun.COM 		prev->ml_size += size + next->ml_size;
115*11474SJonathan.Adams@Sun.COM 		prev->ml_next = next->ml_next;
1163446Smrj 		memlist_free(next);
1173446Smrj 		return;
1183446Smrj 	}
1193446Smrj 
1203446Smrj 	if (merge_left) {
121*11474SJonathan.Adams@Sun.COM 		prev->ml_size += size;
1223446Smrj 		return;
1233446Smrj 	}
1243446Smrj 
1253446Smrj 	if (merge_right) {
126*11474SJonathan.Adams@Sun.COM 		next->ml_address = addr;
127*11474SJonathan.Adams@Sun.COM 		next->ml_size += size;
1283446Smrj 		return;
1293446Smrj 	}
1303446Smrj 
1313446Smrj 	entry = memlist_alloc();
132*11474SJonathan.Adams@Sun.COM 	entry->ml_address = addr;
133*11474SJonathan.Adams@Sun.COM 	entry->ml_size = size;
1343446Smrj 	if (prev == 0) {
135*11474SJonathan.Adams@Sun.COM 		entry->ml_next = *listp;
1363446Smrj 		*listp = entry;
1373446Smrj 	} else {
138*11474SJonathan.Adams@Sun.COM 		entry->ml_next = next;
139*11474SJonathan.Adams@Sun.COM 		prev->ml_next = entry;
1403446Smrj 	}
1413446Smrj }
1423446Smrj 
1433446Smrj /*
1448420SDana.Myers@Sun.COM  * Delete memlist entries, assuming list sorted by address
1453446Smrj  */
1468420SDana.Myers@Sun.COM 
1478420SDana.Myers@Sun.COM #define	MIN(a, b)	((a) < (b) ? (a) : (b))
1488420SDana.Myers@Sun.COM #define	MAX(a, b)	((a) > (b) ? (a) : (b))
1498420SDana.Myers@Sun.COM #define	IN_RANGE(a, b, e) ((a) >= (b) && (a) <= (e))
1508420SDana.Myers@Sun.COM 
1513446Smrj int
memlist_remove(struct memlist ** listp,uint64_t addr,uint64_t size)1523446Smrj memlist_remove(struct memlist **listp, uint64_t addr, uint64_t size)
1533446Smrj {
1548420SDana.Myers@Sun.COM 	struct memlist *prev = 0;
1558420SDana.Myers@Sun.COM 	struct memlist *chunk;
1568420SDana.Myers@Sun.COM 	uint64_t rem_begin, rem_end;
1578420SDana.Myers@Sun.COM 	uint64_t chunk_begin, chunk_end;
1588420SDana.Myers@Sun.COM 	int begin_in_chunk, end_in_chunk;
1598420SDana.Myers@Sun.COM 
1608420SDana.Myers@Sun.COM 
1618420SDana.Myers@Sun.COM 	/* ignore removal of zero-length item */
1628420SDana.Myers@Sun.COM 	if (size == 0)
1638420SDana.Myers@Sun.COM 		return (0);
1643446Smrj 
1658420SDana.Myers@Sun.COM 	/* also inherently ignore a zero-length list */
1668420SDana.Myers@Sun.COM 	rem_begin = addr;
1678420SDana.Myers@Sun.COM 	rem_end = addr + size - 1;
1688420SDana.Myers@Sun.COM 	chunk = *listp;
1698420SDana.Myers@Sun.COM 	while (chunk) {
170*11474SJonathan.Adams@Sun.COM 		chunk_begin = chunk->ml_address;
171*11474SJonathan.Adams@Sun.COM 		chunk_end = chunk->ml_address + chunk->ml_size - 1;
1728420SDana.Myers@Sun.COM 		begin_in_chunk = IN_RANGE(rem_begin, chunk_begin, chunk_end);
1738420SDana.Myers@Sun.COM 		end_in_chunk = IN_RANGE(rem_end, chunk_begin, chunk_end);
1748420SDana.Myers@Sun.COM 
1758420SDana.Myers@Sun.COM 		if (rem_begin <= chunk_begin && rem_end >= chunk_end) {
1768420SDana.Myers@Sun.COM 			struct memlist *delete_chunk;
1773446Smrj 
1788420SDana.Myers@Sun.COM 			/* spans entire chunk - delete chunk */
1798420SDana.Myers@Sun.COM 			delete_chunk = chunk;
1808420SDana.Myers@Sun.COM 			if (prev == 0)
181*11474SJonathan.Adams@Sun.COM 				chunk = *listp = chunk->ml_next;
1828420SDana.Myers@Sun.COM 			else
183*11474SJonathan.Adams@Sun.COM 				chunk = prev->ml_next = chunk->ml_next;
1843446Smrj 
1858420SDana.Myers@Sun.COM 			memlist_free(delete_chunk);
1868420SDana.Myers@Sun.COM 			/* skip to start of while-loop */
1878420SDana.Myers@Sun.COM 			continue;
1888420SDana.Myers@Sun.COM 		} else if (begin_in_chunk && end_in_chunk &&
1898420SDana.Myers@Sun.COM 		    chunk_begin != rem_begin && chunk_end != rem_end) {
1908420SDana.Myers@Sun.COM 			struct memlist *new;
1918420SDana.Myers@Sun.COM 			/* split chunk */
1928420SDana.Myers@Sun.COM 			new = memlist_alloc();
193*11474SJonathan.Adams@Sun.COM 			new->ml_address = rem_end + 1;
194*11474SJonathan.Adams@Sun.COM 			new->ml_size = chunk_end - new->ml_address + 1;
195*11474SJonathan.Adams@Sun.COM 			chunk->ml_size = rem_begin - chunk_begin;
196*11474SJonathan.Adams@Sun.COM 			new->ml_next = chunk->ml_next;
197*11474SJonathan.Adams@Sun.COM 			chunk->ml_next = new;
1988420SDana.Myers@Sun.COM 			/* done - break out of while-loop */
1998420SDana.Myers@Sun.COM 			break;
2008420SDana.Myers@Sun.COM 		} else if (begin_in_chunk || end_in_chunk) {
2018420SDana.Myers@Sun.COM 			/* trim chunk */
202*11474SJonathan.Adams@Sun.COM 			chunk->ml_size -= MIN(chunk_end, rem_end) -
2038420SDana.Myers@Sun.COM 			    MAX(chunk_begin, rem_begin) + 1;
2048420SDana.Myers@Sun.COM 			if (rem_begin <= chunk_begin) {
205*11474SJonathan.Adams@Sun.COM 				chunk->ml_address = rem_end + 1;
2068420SDana.Myers@Sun.COM 				break;
2078420SDana.Myers@Sun.COM 			}
2088420SDana.Myers@Sun.COM 			/* fall-through to next chunk */
2098420SDana.Myers@Sun.COM 		}
2108420SDana.Myers@Sun.COM 		prev = chunk;
211*11474SJonathan.Adams@Sun.COM 		chunk = chunk->ml_next;
2123446Smrj 	}
2133446Smrj 
2143446Smrj 	return (0);
2153446Smrj }
2163446Smrj 
2173446Smrj /*
2183446Smrj  * find and claim a memory chunk of given size, first fit
2193446Smrj  */
2203446Smrj uint64_t
memlist_find(struct memlist ** listp,uint64_t size,int align)2213446Smrj memlist_find(struct memlist **listp, uint64_t size, int align)
2223446Smrj {
2236095Sgs150176 	uint64_t delta, total_size;
2243446Smrj 	uint64_t paddr;
2253446Smrj 	struct memlist *prev = 0, *next;
2263446Smrj 
2273446Smrj 	/* find the chunk with sufficient size */
2283446Smrj 	next = *listp;
2296095Sgs150176 	while (next) {
230*11474SJonathan.Adams@Sun.COM 		delta = next->ml_address & ((align != 0) ? (align - 1) : 0);
2316095Sgs150176 		if (delta != 0)
2326095Sgs150176 			total_size = size + align - delta;
2336095Sgs150176 		else
2346095Sgs150176 			total_size = size; /* the addr is already aligned */
235*11474SJonathan.Adams@Sun.COM 		if (next->ml_size >= total_size)
2366095Sgs150176 			break;
2373446Smrj 		prev = next;
238*11474SJonathan.Adams@Sun.COM 		next = prev->ml_next;
2393446Smrj 	}
2403446Smrj 
2413446Smrj 	if (next == 0)
2426095Sgs150176 		return (0);	/* Not found */
2433446Smrj 
244*11474SJonathan.Adams@Sun.COM 	paddr = next->ml_address;
2453446Smrj 	if (delta)
2463446Smrj 		paddr += align - delta;
2473446Smrj 	(void) memlist_remove(listp, paddr, size);
2486095Sgs150176 
2493446Smrj 	return (paddr);
2503446Smrj }
2513446Smrj 
2523446Smrj /*
2536095Sgs150176  * find and claim a memory chunk of given size, starting
2546095Sgs150176  * at a specified address
2556095Sgs150176  */
2566095Sgs150176 uint64_t
memlist_find_with_startaddr(struct memlist ** listp,uint64_t address,uint64_t size,int align)2576095Sgs150176 memlist_find_with_startaddr(struct memlist **listp, uint64_t address,
2586095Sgs150176     uint64_t size, int align)
2596095Sgs150176 {
2606095Sgs150176 	uint64_t delta, total_size;
2616095Sgs150176 	uint64_t paddr;
2626095Sgs150176 	struct memlist *next;
2636095Sgs150176 
2646095Sgs150176 	/* find the chunk starting at 'address' */
2656095Sgs150176 	next = *listp;
266*11474SJonathan.Adams@Sun.COM 	while (next && (next->ml_address != address)) {
267*11474SJonathan.Adams@Sun.COM 		next = next->ml_next;
2686095Sgs150176 	}
2696095Sgs150176 	if (next == 0)
2706095Sgs150176 		return (0);	/* Not found */
2716095Sgs150176 
272*11474SJonathan.Adams@Sun.COM 	delta = next->ml_address & ((align != 0) ? (align - 1) : 0);
2736095Sgs150176 	if (delta != 0)
2746095Sgs150176 		total_size = size + align - delta;
2756095Sgs150176 	else
2766095Sgs150176 		total_size = size;	/* the addr is already aligned */
277*11474SJonathan.Adams@Sun.COM 	if (next->ml_size < total_size)
2786095Sgs150176 		return (0);	/* unsufficient size */
2796095Sgs150176 
280*11474SJonathan.Adams@Sun.COM 	paddr = next->ml_address;
2816095Sgs150176 	if (delta)
2826095Sgs150176 		paddr += align - delta;
2836095Sgs150176 	(void) memlist_remove(listp, paddr, size);
2846095Sgs150176 
2856095Sgs150176 	return (paddr);
2866095Sgs150176 }
2876095Sgs150176 
2886095Sgs150176 /*
28910251SDan.Mick@Sun.COM  * Subsume memlist src into memlist dest
2906095Sgs150176  */
2916095Sgs150176 void
memlist_subsume(struct memlist ** src,struct memlist ** dest)29210251SDan.Mick@Sun.COM memlist_subsume(struct memlist **src, struct memlist **dest)
2936095Sgs150176 {
2946095Sgs150176 	struct memlist *head, *prev;
2956095Sgs150176 
2966095Sgs150176 	head = *src;
2976095Sgs150176 	while (head) {
298*11474SJonathan.Adams@Sun.COM 		memlist_insert(dest, head->ml_address, head->ml_size);
2996095Sgs150176 		prev = head;
300*11474SJonathan.Adams@Sun.COM 		head = head->ml_next;
3016095Sgs150176 		memlist_free(prev);
3026095Sgs150176 	}
3036095Sgs150176 	*src = 0;
3046095Sgs150176 }
3056095Sgs150176 
3066095Sgs150176 /*
30710251SDan.Mick@Sun.COM  * Merge memlist src into memlist dest; don't destroy src
30810251SDan.Mick@Sun.COM  */
30910251SDan.Mick@Sun.COM void
memlist_merge(struct memlist ** src,struct memlist ** dest)31010251SDan.Mick@Sun.COM memlist_merge(struct memlist **src, struct memlist **dest)
31110251SDan.Mick@Sun.COM {
31210251SDan.Mick@Sun.COM 	struct memlist *p;
31310251SDan.Mick@Sun.COM 
31410251SDan.Mick@Sun.COM 	p = *src;
31510251SDan.Mick@Sun.COM 	while (p) {
316*11474SJonathan.Adams@Sun.COM 		memlist_insert(dest, p->ml_address, p->ml_size);
317*11474SJonathan.Adams@Sun.COM 		p = p->ml_next;
31810251SDan.Mick@Sun.COM 	}
31910251SDan.Mick@Sun.COM }
32010251SDan.Mick@Sun.COM 
32110251SDan.Mick@Sun.COM /*
3223446Smrj  * Make a copy of memlist
3233446Smrj  */
3243446Smrj struct memlist *
memlist_dup(struct memlist * listp)3253446Smrj memlist_dup(struct memlist *listp)
3263446Smrj {
3273446Smrj 	struct memlist *head = 0, *prev = 0;
3283446Smrj 
3293446Smrj 	while (listp) {
3303446Smrj 		struct memlist *entry = memlist_alloc();
331*11474SJonathan.Adams@Sun.COM 		entry->ml_address = listp->ml_address;
332*11474SJonathan.Adams@Sun.COM 		entry->ml_size = listp->ml_size;
333*11474SJonathan.Adams@Sun.COM 		entry->ml_next = 0;
3343446Smrj 		if (prev)
335*11474SJonathan.Adams@Sun.COM 			prev->ml_next = entry;
3363446Smrj 		else
3373446Smrj 			head = entry;
3383446Smrj 		prev = entry;
339*11474SJonathan.Adams@Sun.COM 		listp = listp->ml_next;
3403446Smrj 	}
3413446Smrj 
3423446Smrj 	return (head);
3433446Smrj }
3443446Smrj 
3453446Smrj int
memlist_count(struct memlist * listp)3463446Smrj memlist_count(struct memlist *listp)
3473446Smrj {
3483446Smrj 	int count = 0;
3493446Smrj 	while (listp) {
3503446Smrj 		count++;
351*11474SJonathan.Adams@Sun.COM 		listp = listp->ml_next;
3523446Smrj 	}
3533446Smrj 
3543446Smrj 	return (count);
3553446Smrj }
356