1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 * Copyright 2016 Joyent, Inc.
26 */
27
28/*	Copyright (c) 1988 AT&T	*/
29/*	  All Rights Reserved  	*/
30
31#include "lint.h"
32#include "mallint.h"
33#include "mtlib.h"
34
35#define	_misaligned(p)		((unsigned)(p) & 3)
36		/* 4-byte "word" alignment is considered ok in LP64 */
37#define	_nextblk(p, size)	((TREE *)((uintptr_t)(p) + (size)))
38
39/*
40 * memalign(align, nbytes)
41 *
42 * Description:
43 *	Returns a block of specified size on a specified alignment boundary.
44 *
45 * Algorithm:
46 *	Malloc enough to ensure that a block can be aligned correctly.
47 *	Find the alignment point and return the fragments
48 *	before and after the block.
49 *
50 * Errors:
51 *	Returns NULL and sets errno as follows:
52 *	[EINVAL]
53 *		if nbytes = 0,
54 *		or if alignment is misaligned,
55 *		or if the heap has been detectably corrupted.
56 *	[ENOMEM]
57 *		if the requested memory could not be allocated.
58 */
59
60void *
61memalign(size_t align, size_t nbytes)
62{
63	size_t	 reqsize;	/* Num of bytes to get from malloc() */
64	TREE	*p;		/* Ptr returned from malloc() */
65	TREE	*blk;		/* For addressing fragment blocks */
66	size_t	blksize;	/* Current (shrinking) block size */
67	TREE	*alignedp;	/* Ptr to properly aligned boundary */
68	TREE	*aligned_blk;	/* The block to be returned */
69	size_t	frag_size;	/* size of fragments fore and aft */
70	size_t	 x;
71
72	if (!primary_link_map) {
73		errno = ENOTSUP;
74		return (NULL);
75	}
76
77	/*
78	 * check for valid size and alignment parameters
79	 * MAX_ALIGN check prevents overflow in later calculation.
80	 */
81	if (nbytes == 0 || _misaligned(align) || align == 0 ||
82	    align > MAX_ALIGN) {
83		errno = EINVAL;
84		return (NULL);
85	}
86
87	/*
88	 * Malloc enough memory to guarantee that the result can be
89	 * aligned correctly. The worst case is when malloc returns
90	 * a block so close to the next alignment boundary that a
91	 * fragment of minimum size cannot be created.  In order to
92	 * make sure we can handle this, we need to force the
93	 * alignment to be at least as large as the minimum frag size
94	 * (MINSIZE + WORDSIZE).
95	 */
96
97	/* check for size that could overflow calculations */
98	if (nbytes > MAX_MALLOC) {
99		errno = ENOMEM;
100		return (NULL);
101	}
102	ROUND(nbytes);
103	if (nbytes < MINSIZE)
104		nbytes = MINSIZE;
105	ROUND(align);
106	while (align < MINSIZE + WORDSIZE)
107		align <<= 1;
108	reqsize = nbytes + align + (MINSIZE + WORDSIZE);
109
110	/* check for overflow */
111	if (reqsize < nbytes) {
112		errno = ENOMEM;
113		return (NULL);
114	}
115
116	p = (TREE *)malloc(reqsize);
117	if (p == (TREE *)NULL) {
118		/* malloc sets errno */
119		return (NULL);
120	}
121	(void) mutex_lock(&libc_malloc_lock);
122
123	/*
124	 * get size of the entire block (overhead and all)
125	 */
126	blk = BLOCK(p);			/* back up to get length word */
127	blksize = SIZE(blk);
128	CLRBITS01(blksize);
129
130	/*
131	 * locate the proper alignment boundary within the block.
132	 */
133	x = (size_t)p;
134	if (x % align != 0)
135		x += align - (x % align);
136	alignedp = (TREE *)x;
137	aligned_blk = BLOCK(alignedp);
138
139	/*
140	 * Check out the space to the left of the alignment
141	 * boundary, and split off a fragment if necessary.
142	 */
143	frag_size = (size_t)aligned_blk - (size_t)blk;
144	if (frag_size != 0) {
145		/*
146		 * Create a fragment to the left of the aligned block.
147		 */
148		if (frag_size < MINSIZE + WORDSIZE) {
149			/*
150			 * Not enough space. So make the split
151			 * at the other end of the alignment unit.
152			 * We know this yields enough space, because
153			 * we forced align >= MINSIZE + WORDSIZE above.
154			 */
155			frag_size += align;
156			aligned_blk = _nextblk(aligned_blk, align);
157		}
158		blksize -= frag_size;
159		SIZE(aligned_blk) = blksize | BIT0;
160		frag_size -= WORDSIZE;
161		SIZE(blk) = frag_size | BIT0 | ISBIT1(SIZE(blk));
162		_free_unlocked(DATA(blk));
163	}
164
165	/*
166	 * Is there a (sufficiently large) fragment to the
167	 * right of the aligned block?
168	 */
169	frag_size = blksize - nbytes;
170	if (frag_size >= MINSIZE + WORDSIZE) {
171		/*
172		 * split and free a fragment on the right
173		 */
174		blksize = SIZE(aligned_blk);
175		SIZE(aligned_blk) = nbytes;
176		blk = NEXT(aligned_blk);
177		SETOLD01(SIZE(aligned_blk), blksize);
178		frag_size -= WORDSIZE;
179		SIZE(blk) = frag_size | BIT0;
180		_free_unlocked(DATA(blk));
181	}
182	(void) mutex_unlock(&libc_malloc_lock);
183	return (DATA(aligned_blk));
184}
185
186/*
187 * This is the ISO/IEC C11 version of memalign. We have kept it as a separate
188 * function, but it is basically the same thing. Note that this is implemented
189 * this way to make life easier to libraries which already interpose on
190 * memalign.
191 */
192void *
193aligned_alloc(size_t align, size_t size)
194{
195	return (memalign(align, size));
196}
197