xref: /illumos-gate/usr/src/uts/intel/io/pci/pci_memlist.c (revision cd0d4b40)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
505f867c3Sgs  * Common Development and Distribution License (the "License").
605f867c3Sgs  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
2256f33205SJonathan Adams  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
246ecc4705SAndy Fiddaman  *
256ecc4705SAndy Fiddaman  * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
266ecc4705SAndy Fiddaman  *
277c478bd9Sstevel@tonic-gate  */
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate  * XXX This stuff should be in usr/src/common, to be shared by boot
317c478bd9Sstevel@tonic-gate  * code, kernel DR, and busra stuff.
327c478bd9Sstevel@tonic-gate  *
337c478bd9Sstevel@tonic-gate  * NOTE: We are only using the next-> link. The prev-> link is
347c478bd9Sstevel@tonic-gate  *	not used in the implementation.
357c478bd9Sstevel@tonic-gate  */
367c478bd9Sstevel@tonic-gate #include <sys/types.h>
377c478bd9Sstevel@tonic-gate #include <sys/memlist.h>
387c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
397c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
407c478bd9Sstevel@tonic-gate #include <sys/pci_impl.h>
417c478bd9Sstevel@tonic-gate #include <sys/debug.h>
427c478bd9Sstevel@tonic-gate 
43*cd0d4b40SRobert Mustacchi int pci_memlist_debug;
44*cd0d4b40SRobert Mustacchi #define	dprintf if (pci_memlist_debug) printf
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate void
memlist_dump(struct memlist * listp)477c478bd9Sstevel@tonic-gate memlist_dump(struct memlist *listp)
487c478bd9Sstevel@tonic-gate {
496ecc4705SAndy Fiddaman 	dprintf("memlist 0x%p content: ", (void *)listp);
507c478bd9Sstevel@tonic-gate 	while (listp) {
516ecc4705SAndy Fiddaman 		dprintf("(0x%lx, 0x%lx) ",
526ecc4705SAndy Fiddaman 		    listp->ml_address, listp->ml_size);
5356f33205SJonathan Adams 		listp = listp->ml_next;
547c478bd9Sstevel@tonic-gate 	}
556ecc4705SAndy Fiddaman 	dprintf("\n");
567c478bd9Sstevel@tonic-gate }
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate struct memlist *
memlist_alloc()597c478bd9Sstevel@tonic-gate memlist_alloc()
607c478bd9Sstevel@tonic-gate {
617c478bd9Sstevel@tonic-gate 	return ((struct memlist *)kmem_zalloc(sizeof (struct memlist),
627c478bd9Sstevel@tonic-gate 	    KM_SLEEP));
637c478bd9Sstevel@tonic-gate }
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate void
memlist_free(struct memlist * buf)667c478bd9Sstevel@tonic-gate memlist_free(struct memlist *buf)
677c478bd9Sstevel@tonic-gate {
687c478bd9Sstevel@tonic-gate 	kmem_free(buf, sizeof (struct memlist));
697c478bd9Sstevel@tonic-gate }
707c478bd9Sstevel@tonic-gate 
7105f867c3Sgs void
memlist_free_all(struct memlist ** list)7205f867c3Sgs memlist_free_all(struct memlist **list)
7305f867c3Sgs {
7405f867c3Sgs 	struct memlist  *next, *buf;
7505f867c3Sgs 
7605f867c3Sgs 	next = *list;
7705f867c3Sgs 	while (next) {
7805f867c3Sgs 		buf = next;
7956f33205SJonathan Adams 		next = buf->ml_next;
8005f867c3Sgs 		kmem_free(buf, sizeof (struct memlist));
8105f867c3Sgs 	}
8205f867c3Sgs 	*list = 0;
8305f867c3Sgs }
8405f867c3Sgs 
857c478bd9Sstevel@tonic-gate /* insert in the order of addresses */
867c478bd9Sstevel@tonic-gate void
memlist_insert(struct memlist ** listp,uint64_t addr,uint64_t size)877c478bd9Sstevel@tonic-gate memlist_insert(struct memlist **listp, uint64_t addr, uint64_t size)
887c478bd9Sstevel@tonic-gate {
897c478bd9Sstevel@tonic-gate 	int merge_left, merge_right;
907c478bd9Sstevel@tonic-gate 	struct memlist *entry;
917c478bd9Sstevel@tonic-gate 	struct memlist *prev = 0, *next;
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate 	/* find the location in list */
947c478bd9Sstevel@tonic-gate 	next = *listp;
9556f33205SJonathan Adams 	while (next && next->ml_address <= addr) {
962f283da5SDan Mick 		/*
972f283da5SDan Mick 		 * Drop if this entry already exists, in whole
982f283da5SDan Mick 		 * or in part
992f283da5SDan Mick 		 */
10056f33205SJonathan Adams 		if (next->ml_address <= addr &&
10156f33205SJonathan Adams 		    next->ml_address + next->ml_size >= addr + size) {
1022f283da5SDan Mick 			/* next already contains this entire element; drop */
1032f283da5SDan Mick 			return;
1042f283da5SDan Mick 		}
1052f283da5SDan Mick 
1062f283da5SDan Mick 		/* Is this a "grow block size" request? */
10756f33205SJonathan Adams 		if (next->ml_address == addr) {
1082f283da5SDan Mick 			break;
1092f283da5SDan Mick 		}
1107c478bd9Sstevel@tonic-gate 		prev = next;
11156f33205SJonathan Adams 		next = prev->ml_next;
1127c478bd9Sstevel@tonic-gate 	}
1137c478bd9Sstevel@tonic-gate 
11456f33205SJonathan Adams 	merge_left = (prev && addr == prev->ml_address + prev->ml_size);
11556f33205SJonathan Adams 	merge_right = (next && addr + size == next->ml_address);
1167c478bd9Sstevel@tonic-gate 	if (merge_left && merge_right) {
11756f33205SJonathan Adams 		prev->ml_size += size + next->ml_size;
11856f33205SJonathan Adams 		prev->ml_next = next->ml_next;
1197c478bd9Sstevel@tonic-gate 		memlist_free(next);
1207c478bd9Sstevel@tonic-gate 		return;
1217c478bd9Sstevel@tonic-gate 	}
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate 	if (merge_left) {
12456f33205SJonathan Adams 		prev->ml_size += size;
1257c478bd9Sstevel@tonic-gate 		return;
1267c478bd9Sstevel@tonic-gate 	}
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate 	if (merge_right) {
12956f33205SJonathan Adams 		next->ml_address = addr;
13056f33205SJonathan Adams 		next->ml_size += size;
1317c478bd9Sstevel@tonic-gate 		return;
1327c478bd9Sstevel@tonic-gate 	}
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate 	entry = memlist_alloc();
13556f33205SJonathan Adams 	entry->ml_address = addr;
13656f33205SJonathan Adams 	entry->ml_size = size;
1377c478bd9Sstevel@tonic-gate 	if (prev == 0) {
13856f33205SJonathan Adams 		entry->ml_next = *listp;
1397c478bd9Sstevel@tonic-gate 		*listp = entry;
1407c478bd9Sstevel@tonic-gate 	} else {
14156f33205SJonathan Adams 		entry->ml_next = next;
14256f33205SJonathan Adams 		prev->ml_next = entry;
1437c478bd9Sstevel@tonic-gate 	}
1447c478bd9Sstevel@tonic-gate }
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate /*
1478fc7923fSDana Myers  * Delete memlist entries, assuming list sorted by address
1487c478bd9Sstevel@tonic-gate  */
1498fc7923fSDana Myers 
1508fc7923fSDana Myers #define	MIN(a, b)	((a) < (b) ? (a) : (b))
1518fc7923fSDana Myers #define	MAX(a, b)	((a) > (b) ? (a) : (b))
1528fc7923fSDana Myers #define	IN_RANGE(a, b, e) ((a) >= (b) && (a) <= (e))
1538fc7923fSDana Myers 
1547c478bd9Sstevel@tonic-gate int
memlist_remove(struct memlist ** listp,uint64_t addr,uint64_t size)1557c478bd9Sstevel@tonic-gate memlist_remove(struct memlist **listp, uint64_t addr, uint64_t size)
1567c478bd9Sstevel@tonic-gate {
1578fc7923fSDana Myers 	struct memlist *prev = 0;
1588fc7923fSDana Myers 	struct memlist *chunk;
1598fc7923fSDana Myers 	uint64_t rem_begin, rem_end;
1608fc7923fSDana Myers 	uint64_t chunk_begin, chunk_end;
1618fc7923fSDana Myers 	int begin_in_chunk, end_in_chunk;
1628fc7923fSDana Myers 
1638fc7923fSDana Myers 
1648fc7923fSDana Myers 	/* ignore removal of zero-length item */
1658fc7923fSDana Myers 	if (size == 0)
1668fc7923fSDana Myers 		return (0);
1678fc7923fSDana Myers 
1688fc7923fSDana Myers 	/* also inherently ignore a zero-length list */
1698fc7923fSDana Myers 	rem_begin = addr;
1708fc7923fSDana Myers 	rem_end = addr + size - 1;
1718fc7923fSDana Myers 	chunk = *listp;
1728fc7923fSDana Myers 	while (chunk) {
17356f33205SJonathan Adams 		chunk_begin = chunk->ml_address;
17456f33205SJonathan Adams 		chunk_end = chunk->ml_address + chunk->ml_size - 1;
1758fc7923fSDana Myers 		begin_in_chunk = IN_RANGE(rem_begin, chunk_begin, chunk_end);
1768fc7923fSDana Myers 		end_in_chunk = IN_RANGE(rem_end, chunk_begin, chunk_end);
1778fc7923fSDana Myers 
1788fc7923fSDana Myers 		if (rem_begin <= chunk_begin && rem_end >= chunk_end) {
1798fc7923fSDana Myers 			struct memlist *delete_chunk;
1808fc7923fSDana Myers 
1818fc7923fSDana Myers 			/* spans entire chunk - delete chunk */
1828fc7923fSDana Myers 			delete_chunk = chunk;
1838fc7923fSDana Myers 			if (prev == 0)
18456f33205SJonathan Adams 				chunk = *listp = chunk->ml_next;
1858fc7923fSDana Myers 			else
18656f33205SJonathan Adams 				chunk = prev->ml_next = chunk->ml_next;
1878fc7923fSDana Myers 
1888fc7923fSDana Myers 			memlist_free(delete_chunk);
1898fc7923fSDana Myers 			/* skip to start of while-loop */
1908fc7923fSDana Myers 			continue;
1918fc7923fSDana Myers 		} else if (begin_in_chunk && end_in_chunk &&
1928fc7923fSDana Myers 		    chunk_begin != rem_begin && chunk_end != rem_end) {
1938fc7923fSDana Myers 			struct memlist *new;
1948fc7923fSDana Myers 			/* split chunk */
1958fc7923fSDana Myers 			new = memlist_alloc();
19656f33205SJonathan Adams 			new->ml_address = rem_end + 1;
19756f33205SJonathan Adams 			new->ml_size = chunk_end - new->ml_address + 1;
19856f33205SJonathan Adams 			chunk->ml_size = rem_begin - chunk_begin;
19956f33205SJonathan Adams 			new->ml_next = chunk->ml_next;
20056f33205SJonathan Adams 			chunk->ml_next = new;
2018fc7923fSDana Myers 			/* done - break out of while-loop */
2028fc7923fSDana Myers 			break;
2038fc7923fSDana Myers 		} else if (begin_in_chunk || end_in_chunk) {
2048fc7923fSDana Myers 			/* trim chunk */
20556f33205SJonathan Adams 			chunk->ml_size -= MIN(chunk_end, rem_end) -
2068fc7923fSDana Myers 			    MAX(chunk_begin, rem_begin) + 1;
2078fc7923fSDana Myers 			if (rem_begin <= chunk_begin) {
20856f33205SJonathan Adams 				chunk->ml_address = rem_end + 1;
2098fc7923fSDana Myers 				break;
2108fc7923fSDana Myers 			}
2118fc7923fSDana Myers 			/* fall-through to next chunk */
2127c478bd9Sstevel@tonic-gate 		}
2138fc7923fSDana Myers 		prev = chunk;
21456f33205SJonathan Adams 		chunk = chunk->ml_next;
2157c478bd9Sstevel@tonic-gate 	}
2168fc7923fSDana Myers 
2177c478bd9Sstevel@tonic-gate 	return (0);
2187c478bd9Sstevel@tonic-gate }
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate /*
2217c478bd9Sstevel@tonic-gate  * find and claim a memory chunk of given size, first fit
2227c478bd9Sstevel@tonic-gate  */
2237c478bd9Sstevel@tonic-gate uint64_t
memlist_find(struct memlist ** listp,uint64_t size,int align)2247c478bd9Sstevel@tonic-gate memlist_find(struct memlist **listp, uint64_t size, int align)
2257c478bd9Sstevel@tonic-gate {
22605f867c3Sgs 	uint64_t delta, total_size;
2277c478bd9Sstevel@tonic-gate 	uint64_t paddr;
2287c478bd9Sstevel@tonic-gate 	struct memlist *prev = 0, *next;
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate 	/* find the chunk with sufficient size */
2317c478bd9Sstevel@tonic-gate 	next = *listp;
23205f867c3Sgs 	while (next) {
23356f33205SJonathan Adams 		delta = next->ml_address & ((align != 0) ? (align - 1) : 0);
23405f867c3Sgs 		if (delta != 0)
23505f867c3Sgs 			total_size = size + align - delta;
23605f867c3Sgs 		else
23705f867c3Sgs 			total_size = size; /* the addr is already aligned */
23856f33205SJonathan Adams 		if (next->ml_size >= total_size)
23905f867c3Sgs 			break;
2407c478bd9Sstevel@tonic-gate 		prev = next;
24156f33205SJonathan Adams 		next = prev->ml_next;
2427c478bd9Sstevel@tonic-gate 	}
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate 	if (next == 0)
24505f867c3Sgs 		return (0);	/* Not found */
2467c478bd9Sstevel@tonic-gate 
24756f33205SJonathan Adams 	paddr = next->ml_address;
2487c478bd9Sstevel@tonic-gate 	if (delta)
2497c478bd9Sstevel@tonic-gate 		paddr += align - delta;
2507c478bd9Sstevel@tonic-gate 	(void) memlist_remove(listp, paddr, size);
25105f867c3Sgs 
2527c478bd9Sstevel@tonic-gate 	return (paddr);
2537c478bd9Sstevel@tonic-gate }
2547c478bd9Sstevel@tonic-gate 
25505f867c3Sgs /*
25605f867c3Sgs  * find and claim a memory chunk of given size, starting
25705f867c3Sgs  * at a specified address
25805f867c3Sgs  */
25905f867c3Sgs uint64_t
memlist_find_with_startaddr(struct memlist ** listp,uint64_t address,uint64_t size,int align)26005f867c3Sgs memlist_find_with_startaddr(struct memlist **listp, uint64_t address,
26105f867c3Sgs     uint64_t size, int align)
26205f867c3Sgs {
26305f867c3Sgs 	uint64_t delta, total_size;
26405f867c3Sgs 	uint64_t paddr;
26505f867c3Sgs 	struct memlist *next;
26605f867c3Sgs 
26705f867c3Sgs 	/* find the chunk starting at 'address' */
26805f867c3Sgs 	next = *listp;
26956f33205SJonathan Adams 	while (next && (next->ml_address != address)) {
27056f33205SJonathan Adams 		next = next->ml_next;
27105f867c3Sgs 	}
27205f867c3Sgs 	if (next == 0)
27305f867c3Sgs 		return (0);	/* Not found */
27405f867c3Sgs 
27556f33205SJonathan Adams 	delta = next->ml_address & ((align != 0) ? (align - 1) : 0);
27605f867c3Sgs 	if (delta != 0)
27705f867c3Sgs 		total_size = size + align - delta;
27805f867c3Sgs 	else
27905f867c3Sgs 		total_size = size;	/* the addr is already aligned */
28056f33205SJonathan Adams 	if (next->ml_size < total_size)
28105f867c3Sgs 		return (0);	/* unsufficient size */
28205f867c3Sgs 
28356f33205SJonathan Adams 	paddr = next->ml_address;
28405f867c3Sgs 	if (delta)
28505f867c3Sgs 		paddr += align - delta;
28605f867c3Sgs 	(void) memlist_remove(listp, paddr, size);
28705f867c3Sgs 
28805f867c3Sgs 	return (paddr);
28905f867c3Sgs }
29005f867c3Sgs 
29105f867c3Sgs /*
2922f283da5SDan Mick  * Subsume memlist src into memlist dest
29305f867c3Sgs  */
29405f867c3Sgs void
memlist_subsume(struct memlist ** src,struct memlist ** dest)2952f283da5SDan Mick memlist_subsume(struct memlist **src, struct memlist **dest)
29605f867c3Sgs {
29705f867c3Sgs 	struct memlist *head, *prev;
29805f867c3Sgs 
29905f867c3Sgs 	head = *src;
30005f867c3Sgs 	while (head) {
30156f33205SJonathan Adams 		memlist_insert(dest, head->ml_address, head->ml_size);
30205f867c3Sgs 		prev = head;
30356f33205SJonathan Adams 		head = head->ml_next;
30405f867c3Sgs 		memlist_free(prev);
30505f867c3Sgs 	}
30605f867c3Sgs 	*src = 0;
30705f867c3Sgs }
30805f867c3Sgs 
3092f283da5SDan Mick /*
3102f283da5SDan Mick  * Merge memlist src into memlist dest; don't destroy src
3112f283da5SDan Mick  */
3122f283da5SDan Mick void
memlist_merge(struct memlist ** src,struct memlist ** dest)3132f283da5SDan Mick memlist_merge(struct memlist **src, struct memlist **dest)
3142f283da5SDan Mick {
3152f283da5SDan Mick 	struct memlist *p;
3162f283da5SDan Mick 
3172f283da5SDan Mick 	p = *src;
3182f283da5SDan Mick 	while (p) {
31956f33205SJonathan Adams 		memlist_insert(dest, p->ml_address, p->ml_size);
32056f33205SJonathan Adams 		p = p->ml_next;
3212f283da5SDan Mick 	}
3222f283da5SDan Mick }
3232f283da5SDan Mick 
3247c478bd9Sstevel@tonic-gate /*
3257c478bd9Sstevel@tonic-gate  * Make a copy of memlist
3267c478bd9Sstevel@tonic-gate  */
3277c478bd9Sstevel@tonic-gate struct memlist *
memlist_dup(struct memlist * listp)3287c478bd9Sstevel@tonic-gate memlist_dup(struct memlist *listp)
3297c478bd9Sstevel@tonic-gate {
3307c478bd9Sstevel@tonic-gate 	struct memlist *head = 0, *prev = 0;
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 	while (listp) {
3337c478bd9Sstevel@tonic-gate 		struct memlist *entry = memlist_alloc();
33456f33205SJonathan Adams 		entry->ml_address = listp->ml_address;
33556f33205SJonathan Adams 		entry->ml_size = listp->ml_size;
33656f33205SJonathan Adams 		entry->ml_next = 0;
3377c478bd9Sstevel@tonic-gate 		if (prev)
33856f33205SJonathan Adams 			prev->ml_next = entry;
3397c478bd9Sstevel@tonic-gate 		else
3407c478bd9Sstevel@tonic-gate 			head = entry;
3417c478bd9Sstevel@tonic-gate 		prev = entry;
34256f33205SJonathan Adams 		listp = listp->ml_next;
3437c478bd9Sstevel@tonic-gate 	}
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate 	return (head);
3467c478bd9Sstevel@tonic-gate }
3477c478bd9Sstevel@tonic-gate 
3487c478bd9Sstevel@tonic-gate int
memlist_count(struct memlist * listp)3497c478bd9Sstevel@tonic-gate memlist_count(struct memlist *listp)
3507c478bd9Sstevel@tonic-gate {
3517c478bd9Sstevel@tonic-gate 	int count = 0;
3527c478bd9Sstevel@tonic-gate 	while (listp) {
3537c478bd9Sstevel@tonic-gate 		count++;
35456f33205SJonathan Adams 		listp = listp->ml_next;
3557c478bd9Sstevel@tonic-gate 	}
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate 	return (count);
3587c478bd9Sstevel@tonic-gate }
359