aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/bhyve/pci_emul.h
blob: 14a8fe93634160229e8f2a00f8271c779674742d (plain) (blame)
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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
/*-
 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
 *
 * Copyright (c) 2011 NetApp, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD$
 */

#ifndef _PCI_EMUL_H_
#define _PCI_EMUL_H_

#include <sys/types.h>
#include <sys/queue.h>
#include <sys/kernel.h>
#include <sys/nv.h>
#include <sys/_pthreadtypes.h>

#include <dev/pci/pcireg.h>

#include <assert.h>

#define	PCI_BARMAX	PCIR_MAX_BAR_0	/* BAR registers in a Type 0 header */
#define PCI_BARMAX_WITH_ROM (PCI_BARMAX + 1)
#define PCI_ROM_IDX (PCI_BARMAX + 1)

struct vmctx;
struct pci_devinst;
struct memory_region;
struct vm_snapshot_meta;

struct pci_devemu {
	char      *pe_emu;		/* Name of device emulation */

	/* instance creation */
	int       (*pe_init)(struct vmctx *, struct pci_devinst *,
			     nvlist_t *);
	int	(*pe_legacy_config)(nvlist_t *, const char *);
	const char *pe_alias;

	/* ACPI DSDT enumeration */
	void	(*pe_write_dsdt)(struct pci_devinst *);

	/* config space read/write callbacks */
	int	(*pe_cfgwrite)(struct vmctx *ctx, int vcpu,
			       struct pci_devinst *pi, int offset,
			       int bytes, uint32_t val);
	int	(*pe_cfgread)(struct vmctx *ctx, int vcpu,
			      struct pci_devinst *pi, int offset,
			      int bytes, uint32_t *retval);

	/* BAR read/write callbacks */
	void      (*pe_barwrite)(struct vmctx *ctx, int vcpu,
				 struct pci_devinst *pi, int baridx,
				 uint64_t offset, int size, uint64_t value);
	uint64_t  (*pe_barread)(struct vmctx *ctx, int vcpu,
				struct pci_devinst *pi, int baridx,
				uint64_t offset, int size);

	void	(*pe_baraddr)(struct vmctx *ctx, struct pci_devinst *pi,
			      int baridx, int enabled, uint64_t address);

	/* Save/restore device state */
	int	(*pe_snapshot)(struct vm_snapshot_meta *meta);
	int	(*pe_pause)(struct vmctx *ctx, struct pci_devinst *pi);
	int	(*pe_resume)(struct vmctx *ctx, struct pci_devinst *pi);

};
#define PCI_EMUL_SET(x)   DATA_SET(pci_devemu_set, x);

enum pcibar_type {
	PCIBAR_NONE,
	PCIBAR_IO,
	PCIBAR_MEM32,
	PCIBAR_MEM64,
	PCIBAR_MEMHI64,
	PCIBAR_ROM,
};

struct pcibar {
	enum pcibar_type	type;		/* io or memory */
	uint64_t		size;
	uint64_t		addr;
	uint8_t			lobits;
};

#define PI_NAMESZ	40

struct msix_table_entry {
	uint64_t	addr;
	uint32_t	msg_data;
	uint32_t	vector_control;
} __packed;

/*
 * In case the structure is modified to hold extra information, use a define
 * for the size that should be emulated.
 */
#define	MSIX_TABLE_ENTRY_SIZE	16
#define MAX_MSIX_TABLE_ENTRIES	2048
#define	PBA_SIZE(msgnum)	(roundup2((msgnum), 64) / 8)

enum lintr_stat {
	IDLE,
	ASSERTED,
	PENDING
};

struct pci_devinst {
	struct pci_devemu *pi_d;
	struct vmctx *pi_vmctx;
	uint8_t	  pi_bus, pi_slot, pi_func;
	char	  pi_name[PI_NAMESZ];
	int	  pi_bar_getsize;
	int	  pi_prevcap;
	int	  pi_capend;

	struct {
		int8_t    	pin;
		enum lintr_stat	state;
		int		pirq_pin;
		int	  	ioapic_irq;
		pthread_mutex_t	lock;
	} pi_lintr;

	struct {
		int		enabled;
		uint64_t	addr;
		uint64_t	msg_data;
		int		maxmsgnum;
	} pi_msi;

	struct {
		int	enabled;
		int	table_bar;
		int	pba_bar;
		uint32_t table_offset;
		int	table_count;
		uint32_t pba_offset;
		int	pba_size;
		int	function_mask;
		struct msix_table_entry *table;	/* allocated at runtime */
		uint8_t *mapped_addr;
		size_t	mapped_size;
	} pi_msix;

	void      *pi_arg;		/* devemu-private data */

	u_char	  pi_cfgdata[PCI_REGMAX + 1];
	/* ROM is handled like a BAR */
	struct pcibar pi_bar[PCI_BARMAX_WITH_ROM + 1];
	uint64_t pi_romoffset;
};

struct msicap {
	uint8_t		capid;
	uint8_t		nextptr;
	uint16_t	msgctrl;
	uint32_t	addrlo;
	uint32_t	addrhi;
	uint16_t	msgdata;
} __packed;
static_assert(sizeof(struct msicap) == 14, "compile-time assertion failed");

struct msixcap {
	uint8_t		capid;
	uint8_t		nextptr;
	uint16_t	msgctrl;
	uint32_t	table_info;	/* bar index and offset within it */
	uint32_t	pba_info;	/* bar index and offset within it */
} __packed;
static_assert(sizeof(struct msixcap) == 12, "compile-time assertion failed");

struct pciecap {
	uint8_t		capid;
	uint8_t		nextptr;
	uint16_t	pcie_capabilities;

	uint32_t	dev_capabilities;	/* all devices */
	uint16_t	dev_control;
	uint16_t	dev_status;

	uint32_t	link_capabilities;	/* devices with links */
	uint16_t	link_control;
	uint16_t	link_status;

	uint32_t	slot_capabilities;	/* ports with slots */
	uint16_t	slot_control;
	uint16_t	slot_status;

	uint16_t	root_control;		/* root ports */
	uint16_t	root_capabilities;
	uint32_t	root_status;

	uint32_t	dev_capabilities2;	/* all devices */
	uint16_t	dev_control2;
	uint16_t	dev_status2;

	uint32_t	link_capabilities2;	/* devices with links */
	uint16_t	link_control2;
	uint16_t	link_status2;

	uint32_t	slot_capabilities2;	/* ports with slots */
	uint16_t	slot_control2;
	uint16_t	slot_status2;
} __packed;
static_assert(sizeof(struct pciecap) == 60, "compile-time assertion failed");

typedef void (*pci_lintr_cb)(int b, int s, int pin, int pirq_pin,
    int ioapic_irq, void *arg);

int	init_pci(struct vmctx *ctx);
void	pci_callback(void);
int	pci_emul_alloc_bar(struct pci_devinst *pdi, int idx,
	    enum pcibar_type type, uint64_t size);
int 	pci_emul_alloc_rom(struct pci_devinst *const pdi, const uint64_t size,
    	    void **const addr);
int	pci_emul_add_msicap(struct pci_devinst *pi, int msgnum);
int	pci_emul_add_pciecap(struct pci_devinst *pi, int pcie_device_type);
void	pci_emul_capwrite(struct pci_devinst *pi, int offset, int bytes,
	    uint32_t val, uint8_t capoff, int capid);
void	pci_emul_cmd_changed(struct pci_devinst *pi, uint16_t old);
void	pci_generate_msi(struct pci_devinst *pi, int msgnum);
void	pci_generate_msix(struct pci_devinst *pi, int msgnum);
void	pci_lintr_assert(struct pci_devinst *pi);
void	pci_lintr_deassert(struct pci_devinst *pi);
void	pci_lintr_request(struct pci_devinst *pi);
int	pci_msi_enabled(struct pci_devinst *pi);
int	pci_msix_enabled(struct pci_devinst *pi);
int	pci_msix_table_bar(struct pci_devinst *pi);
int	pci_msix_pba_bar(struct pci_devinst *pi);
int	pci_msi_maxmsgnum(struct pci_devinst *pi);
int	pci_parse_legacy_config(nvlist_t *nvl, const char *opt);
int	pci_parse_slot(char *opt);
void    pci_print_supported_devices(void);
void	pci_populate_msicap(struct msicap *cap, int msgs, int nextptr);
int	pci_emul_add_msixcap(struct pci_devinst *pi, int msgnum, int barnum);
int	pci_emul_msix_twrite(struct pci_devinst *pi, uint64_t offset, int size,
			     uint64_t value);
uint64_t pci_emul_msix_tread(struct pci_devinst *pi, uint64_t offset, int size);
int	pci_count_lintr(int bus);
void	pci_walk_lintr(int bus, pci_lintr_cb cb, void *arg);
void	pci_write_dsdt(void);
uint64_t pci_ecfg_base(void);
int	pci_bus_configured(int bus);
#ifdef BHYVE_SNAPSHOT
int	pci_snapshot(struct vm_snapshot_meta *meta);
int	pci_pause(struct vmctx *ctx, const char *dev_name);
int	pci_resume(struct vmctx *ctx, const char *dev_name);
#endif

static __inline void
pci_set_cfgdata8(struct pci_devinst *pi, int offset, uint8_t val)
{
	assert(offset <= PCI_REGMAX);
	*(uint8_t *)(pi->pi_cfgdata + offset) = val;
}

static __inline void
pci_set_cfgdata16(struct pci_devinst *pi, int offset, uint16_t val)
{
	assert(offset <= (PCI_REGMAX - 1) && (offset & 1) == 0);
	*(uint16_t *)(pi->pi_cfgdata + offset) = val;
}

static __inline void
pci_set_cfgdata32(struct pci_devinst *pi, int offset, uint32_t val)
{
	assert(offset <= (PCI_REGMAX - 3) && (offset & 3) == 0);
	*(uint32_t *)(pi->pi_cfgdata + offset) = val;
}

static __inline uint8_t
pci_get_cfgdata8(struct pci_devinst *pi, int offset)
{
	assert(offset <= PCI_REGMAX);
	return (*(uint8_t *)(pi->pi_cfgdata + offset));
}

static __inline uint16_t
pci_get_cfgdata16(struct pci_devinst *pi, int offset)
{
	assert(offset <= (PCI_REGMAX - 1) && (offset & 1) == 0);
	return (*(uint16_t *)(pi->pi_cfgdata + offset));
}

static __inline uint32_t
pci_get_cfgdata32(struct pci_devinst *pi, int offset)
{
	assert(offset <= (PCI_REGMAX - 3) && (offset & 3) == 0);
	return (*(uint32_t *)(pi->pi_cfgdata + offset));
}

#endif /* _PCI_EMUL_H_ */