summaryrefslogtreecommitdiff
path: root/src/soc/intel/braswell/spi_loading.c
blob: 0ebe72dd7f4b5c5b7c80971c67ec5f5d8651f1b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2013 Google Inc.
 * Copyright (C) 2015 Intel Corp.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */


#include <arch/byteorder.h>
#include <boot/coreboot_tables.h>
#include <cbmem.h>
#include <cbfs.h>
#include <console/console.h>
#include <stdlib.h>
#include <string.h>
#include <vendorcode/google/chromeos/chromeos.h>

#define CACHELINE_SIZE 64
#define INTRA_CACHELINE_MASK (CACHELINE_SIZE - 1)
#define CACHELINE_MASK (~INTRA_CACHELINE_MASK)

static void *find_mirror_buffer(int len)
{
	int nentries;
	int i;
	struct lb_memory *mem;
	void *buffer;

	len = ALIGN(len, 4096);

	mem = get_lb_mem();
	nentries = (mem->size - sizeof(*mem)) / sizeof(mem->map[0]);

	/*
	 * Find the highest RAM entry that accommodates the lenth provide
	 * while falling below 4GiB.
	 */
	buffer = NULL;
	for (i = 0; i < nentries; i++) {
		const uint64_t max_addr = 1ULL << 32;
		uint64_t start;
		uint64_t size;
		struct lb_memory_range *r;

		r = &mem->map[i];

		if (r->type != LB_MEM_RAM)
			continue;

		start = unpack_lb64(r->start);
		if (start >= max_addr)
			continue;

		size = unpack_lb64(r->size);
		if (size < len)
			continue;

		/* Adjust size of buffer if range exceeds max address. */
		if (start + size > max_addr)
			size = max_addr - start;

		if (size < len)
			continue;

		buffer = (void *)(uintptr_t)(start + size - len);
	}

	return buffer;
}

/*
 * Mirror the payload file to the default SMM location if it is small enough.
 * The default SMM region can be used since no one is using the memory at this
 * location at this stage in the boot.
 */
static void *spi_mirror(void *file_start, int file_len)
{
	int alignment_diff;
	char *src;
	char *dest;

	alignment_diff = (INTRA_CACHELINE_MASK & (long)file_start);

	/*
	 * Adjust file length so that the start and end points are aligned to a
	 * cacheline. Coupled with the ROM caching in the CPU the SPI hardware
	 * will read and cache full length cachelines. It will also prefetch
	 * data as well. Once things are mirrored in memory all accesses should
	 * hit the CPUs cache.
	 */
	file_len += alignment_diff;
	file_len = ALIGN(file_len, CACHELINE_SIZE);

	printk(BIOS_DEBUG, "Payload aligned size: 0x%x\n", file_len);

	dest = find_mirror_buffer(file_len);

	/*
	 * Just pass back the pointer to ROM space if a buffer could not
	 * be found to mirror into.
	 */
	if (dest == NULL)
		return file_start;

	src = (void *)(CACHELINE_MASK & (long)file_start);
	/*
	 * Note that if mempcy is not using 32-bit moves the performance will
	 * degrade because the SPI hardware prefetchers look for
	 * cacheline-aligned 32-bit accesses to kick in.
	 */
	memcpy(dest, src, file_len);

	/* Provide pointer into mirrored space. */
	return &dest[alignment_diff];
}

void *cbfs_load_payload(struct cbfs_media *media, const char *name)
{
	int file_len;
	void *file_start;
	struct cbfs_file *file;

	file_start = vboot_get_payload(&file_len);

	if (file_start != NULL)
		return spi_mirror(file_start, file_len);

	file = cbfs_get_file(media, name);

	if (file == NULL)
		return NULL;

	if (ntohl(file->type) != CBFS_TYPE_PAYLOAD)
		return NULL;

	file_len = ntohl(file->len);

	file_start = CBFS_SUBHEADER(file);

	return spi_mirror(file_start, file_len);
}