summaryrefslogtreecommitdiff
path: root/lib/vbexport/boot_device.c
blob: adb67e3253c104b50151d4d66d174cdd350f7800 (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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
/*
 * 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>
#ifdef CONFIG_MMC
#include <mmc.h>
#endif
#include <part.h>
#include <usb.h>
#include <chromeos/common.h>
#include <linux/list.h>

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

#define PREFIX			"boot_device: "

/*
 * Total number of storage devices, USB + MMC + reserved.
 * MMC is 2 now. Reserve extra 3 for margin of safety.
 */
#define MAX_DISK_INFO		(USB_MAX_STOR_DEV + 2 + 3)

/* 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,
};

static uint32_t get_dev_flags(const block_dev_desc_t *dev)
{
	return dev->removable ? VB_DISK_FLAG_REMOVABLE : VB_DISK_FLAG_FIXED;
}

static const char *get_dev_name(const block_dev_desc_t *dev)
{
	/*
	 * See the definitions of IF_TYPE_* in /include/part.h.
	 * 8 is max size of strings, the "UNKNOWN".
	 */
	static const char all_disk_types[IF_TYPE_MAX][8] = {
		"UNKNOWN", "IDE", "SCSI", "ATAPI", "USB",
		"DOC", "MMC", "SD", "SATA"};

	if (dev->if_type >= IF_TYPE_MAX) {
		return all_disk_types[0];
	}
	return all_disk_types[dev->if_type];
}

static void init_usb_storage(void)
{
	/*
	 * We should stop all USB devices first. Otherwise we can't detect any
	 * new devices.
	 */
	usb_stop();

	if (usb_init() >= 0)
		usb_stor_scan(/*mode=*/1);
}

block_dev_desc_t *iterate_internal_devices(int *index_ptr)
{
#ifdef CONFIG_MMC
	struct mmc *mmc;
	int index;

	for (index = *index_ptr; (mmc = find_mmc_device(index)); index++) {
		/* Skip device that cannot be initialized */
		if (!mmc_init(mmc))
			break;
	}

	/*
	 * find_mmc_device() returns a pointer from a global linked list. So
	 * &mmc->block_dev is not a dangling pointer after this function
	 * is returned.
	 */

	*index_ptr = index + 1;
	if (mmc)
		return &mmc->block_dev;
#endif
#ifdef CONFIG_CMD_IDE
#ifdef CONFIG_MMC
	/* TODO(reinauer) Fix index handling first */
#error "MMC and IDE can not be enabled at the same time right now."
#endif
	block_dev_desc_t *ide;
	ide = ide_get_dev(*index_ptr);
	*index_ptr = *index_ptr + 1;

	if (ide)
		return ide;
#endif
	return NULL;
}

block_dev_desc_t *iterate_usb_device(int *index_ptr)
{
	return usb_stor_get_dev((*index_ptr)++);
}

/*
 * This appends [dev] to [info] and increments [count_ptr] if [disk_flags] is a
 * subset of the flags of [dev].
 */
void add_device_if_flags_match(block_dev_desc_t *dev, const uint32_t disk_flags,
		VbDiskInfo *info, uint32_t *count_ptr)
{
	uint32_t flags = get_dev_flags(dev);

	/*
	 * Only add this storage device if the properties of disk_flags is a
	 * subset of the properties of flags.
	 */
	if ((flags & disk_flags) != disk_flags)
		return;

	info->handle = (VbExDiskHandle_t)dev;
	info->bytes_per_lba = dev->blksz;
	info->lba_count = dev->lba;
	info->flags = flags;
	info->name = get_dev_name(dev);
	(*count_ptr)++;
}

VbError_t VbExDiskGetInfo(VbDiskInfo** infos_ptr, uint32_t* count_ptr,
                          uint32_t disk_flags)
{
	VbDiskInfo *infos;
	uint32_t count, max_count;
	int iterator_state;
	block_dev_desc_t *dev;

	*infos_ptr = NULL;
	*count_ptr = 0;

	/* We return as many disk infos as possible. */
	/*
	 * We assume there is only one non-removable storage device. So if the
	 * caller asks for non-removable devices, we return at most one device.
	 * Otherwise we return at most MAX_DISK_INFO device.
	 */
	max_count = (disk_flags & VB_DISK_FLAG_REMOVABLE) ? MAX_DISK_INFO : 1;

	/* count is the number of device returned */
	infos = (VbDiskInfo *)VbExMalloc(sizeof(VbDiskInfo) * max_count);
	count = 0;

	iterator_state = 0;
	while (count < max_count && (dev = iterate_internal_devices(&iterator_state)))
		add_device_if_flags_match(dev, disk_flags,
				infos + count, &count);

	/* Skip probing USB device if not require removable devices. */
        if (count < max_count && (disk_flags & VB_DISK_FLAG_REMOVABLE)) {
		/* Initialize USB device before probing them. */
		init_usb_storage();

		iterator_state = 0;
		while (count < max_count &&
				(dev = iterate_usb_device(&iterator_state)))
			add_device_if_flags_match(dev, disk_flags,
					infos + count, &count);
	}

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

	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(dev->dev, lba_start, lba_count, buffer)
			!= lba_count)
		return VBERROR_DISK_WRITE_ERROR;

	return VBERROR_SUCCESS;
}