162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/efi.h>
462306a36Sopenharmony_ci#include <linux/memblock.h>
562306a36Sopenharmony_ci#include <linux/spinlock.h>
662306a36Sopenharmony_ci#include <asm/unaccepted_memory.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/* Protects unaccepted memory bitmap and accepting_list */
962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(unaccepted_memory_lock);
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistruct accept_range {
1262306a36Sopenharmony_ci	struct list_head list;
1362306a36Sopenharmony_ci	unsigned long start;
1462306a36Sopenharmony_ci	unsigned long end;
1562306a36Sopenharmony_ci};
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic LIST_HEAD(accepting_list);
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*
2062306a36Sopenharmony_ci * accept_memory() -- Consult bitmap and accept the memory if needed.
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * Only memory that is explicitly marked as unaccepted in the bitmap requires
2362306a36Sopenharmony_ci * an action. All the remaining memory is implicitly accepted and doesn't need
2462306a36Sopenharmony_ci * acceptance.
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * No need to accept:
2762306a36Sopenharmony_ci *  - anything if the system has no unaccepted table;
2862306a36Sopenharmony_ci *  - memory that is below phys_base;
2962306a36Sopenharmony_ci *  - memory that is above the memory that addressable by the bitmap;
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_civoid accept_memory(phys_addr_t start, phys_addr_t end)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct efi_unaccepted_memory *unaccepted;
3462306a36Sopenharmony_ci	unsigned long range_start, range_end;
3562306a36Sopenharmony_ci	struct accept_range range, *entry;
3662306a36Sopenharmony_ci	unsigned long flags;
3762306a36Sopenharmony_ci	u64 unit_size;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	unaccepted = efi_get_unaccepted_table();
4062306a36Sopenharmony_ci	if (!unaccepted)
4162306a36Sopenharmony_ci		return;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	unit_size = unaccepted->unit_size;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	/*
4662306a36Sopenharmony_ci	 * Only care for the part of the range that is represented
4762306a36Sopenharmony_ci	 * in the bitmap.
4862306a36Sopenharmony_ci	 */
4962306a36Sopenharmony_ci	if (start < unaccepted->phys_base)
5062306a36Sopenharmony_ci		start = unaccepted->phys_base;
5162306a36Sopenharmony_ci	if (end < unaccepted->phys_base)
5262306a36Sopenharmony_ci		return;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/* Translate to offsets from the beginning of the bitmap */
5562306a36Sopenharmony_ci	start -= unaccepted->phys_base;
5662306a36Sopenharmony_ci	end -= unaccepted->phys_base;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	/*
5962306a36Sopenharmony_ci	 * load_unaligned_zeropad() can lead to unwanted loads across page
6062306a36Sopenharmony_ci	 * boundaries. The unwanted loads are typically harmless. But, they
6162306a36Sopenharmony_ci	 * might be made to totally unrelated or even unmapped memory.
6262306a36Sopenharmony_ci	 * load_unaligned_zeropad() relies on exception fixup (#PF, #GP and now
6362306a36Sopenharmony_ci	 * #VE) to recover from these unwanted loads.
6462306a36Sopenharmony_ci	 *
6562306a36Sopenharmony_ci	 * But, this approach does not work for unaccepted memory. For TDX, a
6662306a36Sopenharmony_ci	 * load from unaccepted memory will not lead to a recoverable exception
6762306a36Sopenharmony_ci	 * within the guest. The guest will exit to the VMM where the only
6862306a36Sopenharmony_ci	 * recourse is to terminate the guest.
6962306a36Sopenharmony_ci	 *
7062306a36Sopenharmony_ci	 * There are two parts to fix this issue and comprehensively avoid
7162306a36Sopenharmony_ci	 * access to unaccepted memory. Together these ensure that an extra
7262306a36Sopenharmony_ci	 * "guard" page is accepted in addition to the memory that needs to be
7362306a36Sopenharmony_ci	 * used:
7462306a36Sopenharmony_ci	 *
7562306a36Sopenharmony_ci	 * 1. Implicitly extend the range_contains_unaccepted_memory(start, end)
7662306a36Sopenharmony_ci	 *    checks up to end+unit_size if 'end' is aligned on a unit_size
7762306a36Sopenharmony_ci	 *    boundary.
7862306a36Sopenharmony_ci	 *
7962306a36Sopenharmony_ci	 * 2. Implicitly extend accept_memory(start, end) to end+unit_size if
8062306a36Sopenharmony_ci	 *    'end' is aligned on a unit_size boundary. (immediately following
8162306a36Sopenharmony_ci	 *    this comment)
8262306a36Sopenharmony_ci	 */
8362306a36Sopenharmony_ci	if (!(end % unit_size))
8462306a36Sopenharmony_ci		end += unit_size;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/* Make sure not to overrun the bitmap */
8762306a36Sopenharmony_ci	if (end > unaccepted->size * unit_size * BITS_PER_BYTE)
8862306a36Sopenharmony_ci		end = unaccepted->size * unit_size * BITS_PER_BYTE;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	range.start = start / unit_size;
9162306a36Sopenharmony_ci	range.end = DIV_ROUND_UP(end, unit_size);
9262306a36Sopenharmony_ciretry:
9362306a36Sopenharmony_ci	spin_lock_irqsave(&unaccepted_memory_lock, flags);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	/*
9662306a36Sopenharmony_ci	 * Check if anybody works on accepting the same range of the memory.
9762306a36Sopenharmony_ci	 *
9862306a36Sopenharmony_ci	 * The check is done with unit_size granularity. It is crucial to catch
9962306a36Sopenharmony_ci	 * all accept requests to the same unit_size block, even if they don't
10062306a36Sopenharmony_ci	 * overlap on physical address level.
10162306a36Sopenharmony_ci	 */
10262306a36Sopenharmony_ci	list_for_each_entry(entry, &accepting_list, list) {
10362306a36Sopenharmony_ci		if (entry->end <= range.start)
10462306a36Sopenharmony_ci			continue;
10562306a36Sopenharmony_ci		if (entry->start >= range.end)
10662306a36Sopenharmony_ci			continue;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		/*
10962306a36Sopenharmony_ci		 * Somebody else accepting the range. Or at least part of it.
11062306a36Sopenharmony_ci		 *
11162306a36Sopenharmony_ci		 * Drop the lock and retry until it is complete.
11262306a36Sopenharmony_ci		 */
11362306a36Sopenharmony_ci		spin_unlock_irqrestore(&unaccepted_memory_lock, flags);
11462306a36Sopenharmony_ci		goto retry;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	/*
11862306a36Sopenharmony_ci	 * Register that the range is about to be accepted.
11962306a36Sopenharmony_ci	 * Make sure nobody else will accept it.
12062306a36Sopenharmony_ci	 */
12162306a36Sopenharmony_ci	list_add(&range.list, &accepting_list);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	range_start = range.start;
12462306a36Sopenharmony_ci	for_each_set_bitrange_from(range_start, range_end, unaccepted->bitmap,
12562306a36Sopenharmony_ci				   range.end) {
12662306a36Sopenharmony_ci		unsigned long phys_start, phys_end;
12762306a36Sopenharmony_ci		unsigned long len = range_end - range_start;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci		phys_start = range_start * unit_size + unaccepted->phys_base;
13062306a36Sopenharmony_ci		phys_end = range_end * unit_size + unaccepted->phys_base;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci		/*
13362306a36Sopenharmony_ci		 * Keep interrupts disabled until the accept operation is
13462306a36Sopenharmony_ci		 * complete in order to prevent deadlocks.
13562306a36Sopenharmony_ci		 *
13662306a36Sopenharmony_ci		 * Enabling interrupts before calling arch_accept_memory()
13762306a36Sopenharmony_ci		 * creates an opportunity for an interrupt handler to request
13862306a36Sopenharmony_ci		 * acceptance for the same memory. The handler will continuously
13962306a36Sopenharmony_ci		 * spin with interrupts disabled, preventing other task from
14062306a36Sopenharmony_ci		 * making progress with the acceptance process.
14162306a36Sopenharmony_ci		 */
14262306a36Sopenharmony_ci		spin_unlock(&unaccepted_memory_lock);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci		arch_accept_memory(phys_start, phys_end);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		spin_lock(&unaccepted_memory_lock);
14762306a36Sopenharmony_ci		bitmap_clear(unaccepted->bitmap, range_start, len);
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	list_del(&range.list);
15162306a36Sopenharmony_ci	spin_unlock_irqrestore(&unaccepted_memory_lock, flags);
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cibool range_contains_unaccepted_memory(phys_addr_t start, phys_addr_t end)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	struct efi_unaccepted_memory *unaccepted;
15762306a36Sopenharmony_ci	unsigned long flags;
15862306a36Sopenharmony_ci	bool ret = false;
15962306a36Sopenharmony_ci	u64 unit_size;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	unaccepted = efi_get_unaccepted_table();
16262306a36Sopenharmony_ci	if (!unaccepted)
16362306a36Sopenharmony_ci		return false;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	unit_size = unaccepted->unit_size;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	/*
16862306a36Sopenharmony_ci	 * Only care for the part of the range that is represented
16962306a36Sopenharmony_ci	 * in the bitmap.
17062306a36Sopenharmony_ci	 */
17162306a36Sopenharmony_ci	if (start < unaccepted->phys_base)
17262306a36Sopenharmony_ci		start = unaccepted->phys_base;
17362306a36Sopenharmony_ci	if (end < unaccepted->phys_base)
17462306a36Sopenharmony_ci		return false;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/* Translate to offsets from the beginning of the bitmap */
17762306a36Sopenharmony_ci	start -= unaccepted->phys_base;
17862306a36Sopenharmony_ci	end -= unaccepted->phys_base;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/*
18162306a36Sopenharmony_ci	 * Also consider the unaccepted state of the *next* page. See fix #1 in
18262306a36Sopenharmony_ci	 * the comment on load_unaligned_zeropad() in accept_memory().
18362306a36Sopenharmony_ci	 */
18462306a36Sopenharmony_ci	if (!(end % unit_size))
18562306a36Sopenharmony_ci		end += unit_size;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/* Make sure not to overrun the bitmap */
18862306a36Sopenharmony_ci	if (end > unaccepted->size * unit_size * BITS_PER_BYTE)
18962306a36Sopenharmony_ci		end = unaccepted->size * unit_size * BITS_PER_BYTE;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	spin_lock_irqsave(&unaccepted_memory_lock, flags);
19262306a36Sopenharmony_ci	while (start < end) {
19362306a36Sopenharmony_ci		if (test_bit(start / unit_size, unaccepted->bitmap)) {
19462306a36Sopenharmony_ci			ret = true;
19562306a36Sopenharmony_ci			break;
19662306a36Sopenharmony_ci		}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci		start += unit_size;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci	spin_unlock_irqrestore(&unaccepted_memory_lock, flags);
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	return ret;
20362306a36Sopenharmony_ci}
204