summaryrefslogtreecommitdiff
path: root/lib/vbexport/boot_device.c
blob: 11daea21761dad1ec4d85130cb5346b54f8b7464 (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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
/*
 * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 * Alternatively, this software may be distributed under the terms of the
 * GNU General Public License ("GPL") version 2 as published by the Free
 * Software Foundation.
 */

#include <common.h>
#include <part.h>
#include <chromeos/common.h>
#include <linux/list.h>

/* Import the header files from vboot_reference. */
#include <vboot_api.h>

#include "boot_device.h"

#define PREFIX			"boot_device: "

/* Maximum number of devices we can support */
enum {
	MAX_DISK_INFO	= 10,
};

/* TODO Move these definitions to vboot_wrapper.h or somewhere like that. */
enum {
	VBERROR_DISK_NO_DEVICE = 1,
	VBERROR_DISK_OUT_OF_RANGE,
	VBERROR_DISK_READ_ERROR,
	VBERROR_DISK_WRITE_ERROR,
};

/* Boot interfaces that we know about */
struct boot_interface *interface[IF_TYPE_MAX];
int interface_count;

int boot_device_register_interface(struct boot_interface *iface)
{
	if (interface_count >= ARRAY_SIZE(interface))
		return -1;
	interface[interface_count++] = iface;
	return 0;
}

int boot_device_matches(const block_dev_desc_t *dev,
				    uint32_t disk_flags, uint32_t *flags)
{
	*flags = dev->removable ? VB_DISK_FLAG_REMOVABLE :
			VB_DISK_FLAG_FIXED;
	return (*flags & disk_flags) == disk_flags && dev->lba;
}

/**
 * Scan a list of devices and add those which match the supplied disk_flags
 * to the info array.
 *
 * @param name		Peripheral name
 * @param devs		List of devices to check
 * @param count		Number of devices
 * @param disk_flags	Disk flags which must be present for each device added
 * @param info		Output array of matching devices
 * @return number of devices added (0 if none)
 */
static int add_matching_devices(const char *name, block_dev_desc_t **devs,
		int count, uint32_t disk_flags, VbDiskInfo *info)
{
	int added = 0;
	int i;

	for (i = 0; i < count; i++) {
		block_dev_desc_t *dev = devs[i];
		uint32_t flags;

		/*
		* Only add this storage device if the properties of
		* disk_flags is a subset of the properties of flags.
		*/
		if (boot_device_matches(dev, disk_flags, &flags)) {
			info->handle = (VbExDiskHandle_t)dev;
			info->bytes_per_lba = dev->blksz;
			info->lba_count = dev->lba;
			info->flags = flags;
			info->name = name;
			info++, added++;
		}
	}
	return added;
}

VbError_t VbExDiskGetInfo(VbDiskInfo **infos_ptr, uint32_t *count_ptr,
			  uint32_t disk_flags)
{
	VbDiskInfo *infos;
	uint32_t max_count;	/* maximum devices to scan for */
	uint32_t count = 0;	/* number of matching devices found */
	block_dev_desc_t *dev[MAX_DISK_INFO];
	int upto;

	/* We return as many disk infos as possible. */
	max_count = MAX_DISK_INFO;

	infos = (VbDiskInfo *)VbExMalloc(sizeof(VbDiskInfo) * max_count);

	/* Scan through all the interfaces looking for devices */
	for (upto = 0; upto < interface_count && count < max_count; upto++) {
		struct boot_interface *iface = interface[upto];
		int found;

		found = iface->start(disk_flags);
		if (found < 0) {
			VBDEBUG(PREFIX "%s: start() failed\n", iface->name);
			continue;
		}
		if (!found) {
			VBDEBUG(PREFIX "%s - start() returned 0\n",
				iface->name);
			continue;
		}

		found = iface->scan(dev, max_count - count, disk_flags);
		if (found < 0) {
			VBDEBUG(PREFIX "%s: scan() failed\n", iface->name);
			continue;
		}
		assert(found <= max_count - count);

		/* Now record the devices that have the required flags */
		count += add_matching_devices(iface->name, dev, found,
					      disk_flags, infos);
	}

	if (count) {
		*infos_ptr = infos;
		*count_ptr = count;
	} else {
		*infos_ptr = NULL;
		*count_ptr = 0;
		VbExFree(infos);
	}

	/* The operation itself succeeds, despite scan failure all about */
	return VBERROR_SUCCESS;
}

VbError_t VbExDiskFreeInfo(VbDiskInfo *infos, VbExDiskHandle_t preserve_handle)
{
	/* We do nothing for preserve_handle as we keep all the devices on. */
	VbExFree(infos);
	return VBERROR_SUCCESS;
}

VbError_t VbExDiskRead(VbExDiskHandle_t handle, uint64_t lba_start,
                       uint64_t lba_count, void *buffer)
{
	block_dev_desc_t *dev = (block_dev_desc_t *)handle;

	if (dev == NULL)
		return VBERROR_DISK_NO_DEVICE;

	if (lba_start >= dev->lba || lba_start + lba_count > dev->lba)
		return VBERROR_DISK_OUT_OF_RANGE;

	if (dev->block_read(dev->dev, lba_start, lba_count, buffer)
			!= lba_count)
		return VBERROR_DISK_READ_ERROR;

	return VBERROR_SUCCESS;
}

VbError_t VbExDiskWrite(VbExDiskHandle_t handle, uint64_t lba_start,
                        uint64_t lba_count, const void *buffer)
{
	block_dev_desc_t *dev = (block_dev_desc_t *)handle;

	/* TODO Replace the error codes with pre-defined macro. */
	if (dev == NULL)
		return VBERROR_DISK_NO_DEVICE;

	if (lba_start >= dev->lba || lba_start + lba_count > dev->lba)
		return VBERROR_DISK_OUT_OF_RANGE;

	if (!dev->block_write) {
		VBDEBUG(PREFIX "interface (%d) does not support writing.\n",
			(int)dev->if_type);
		return VBERROR_DISK_WRITE_ERROR;
	}
	if (dev->block_write(dev->dev, lba_start, lba_count, buffer)
			!= lba_count)
		return VBERROR_DISK_WRITE_ERROR;

	return VBERROR_SUCCESS;
}

int boot_device_init(void)
{
	int err = 0;

#ifdef CONFIG_CHROMEOS_USB
	err |= boot_device_usb_probe();
#endif
#ifdef CONFIG_MMC
	err |= boot_device_mmc_probe();
#endif
#ifdef CONFIG_CHROMEOS_IDE
	err |= boot_device_ide_probe();
#endif
#ifdef CONFIG_SCSI_AHCI
	err |= boot_device_scsi_probe();
#endif
	return err;
}