aboutsummaryrefslogtreecommitdiff
path: root/vbox.c
blob: 9fd0aa4975607e754803173a42a4298e445a33f4 (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
/*
 * VBMouse - VirtualBox communication routines
 * Copyright (C) 2022 Javier S. Pedro
 *
 * 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; either version 2
 * of the License, or (at your option) any later version.

 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <i86.h>

#include "dlog.h"
#include "utils.h"
#include "int1Apci.h"
#include "int4Bvds.h"
#include "vboxdev.h"
#include "vbox.h"

// Classic PCI defines
#define VBOX_PCI_VEND_ID 0x80ee
#define VBOX_PCI_PROD_ID 0xcafe
enum {
	CFG_COMMAND         = 0x04, /* Word  */
	CFG_INTERRUPT       = 0x3C, /* Byte */
	CFG_BAR0            = 0x10, /* DWord */
	CFG_BAR1            = 0x14, /* DWord */
};

int vbox_init_device(LPVBOXCOMM vb)
{
	int err;
	pcisel pcidev;
	uint16_t command;
	uint32_t bar;

	if ((err = pci_init_bios())) {
		return err;
	}

	if ((err = pci_find_device(&pcidev, VBOX_PCI_VEND_ID, VBOX_PCI_PROD_ID, 0))) {
		return err;
	}

	if ((err = pci_read_config_word(pcidev, CFG_COMMAND, &command))) {
		return err;
	}

	if (!(command & 1)) {
		// The card is not configured
		return -1;
	}

	if ((err = pci_read_config_dword(pcidev, CFG_BAR0, &bar))) {
		return err;
	}

	if (!(bar & 1)) {
		// This is not an IO BAR
		return -2;
	}

	vb->iobase = bar & 0xFFFC;

	return 0;
}

int vbox_init_buffer(LPVBOXCOMM vb, unsigned size)
{
	vb->dds.regionSize = size;
	vb->dds.segOrSelector = FP_SEG(&vb->buf);
	vb->dds.offset = FP_OFF(&vb->buf);
	vb->dds.bufferId = 0;
	vb->dds.physicalAddress = 0;
	vb->vds = false;

	if (vds_available()) {
		// Use the Virtual DMA Service to get the physical address of this buffer
		int err;

		err = vds_lock_dma_buffer_region(&vb->dds, VDS_NO_AUTO_ALLOC);
		if (err) {
			// As far as I have seen, most VDS providers always keep low memory contiguous,
			// so I'm not handling VDS_REGION_NOT_CONTIGUOUS here.
			dlog_print("Error while VDS locking, err=");
			dlog_printd(err);
			dlog_endline();
			return err;
		}

		vb->vds = true;
	} else {
		// If VDS is not available,
		// we assume a 1:1 mapping between linear and physical addresses
		vb->dds.physicalAddress = linear_addr(&vb->buf);
	}

	return 0;
}

int vbox_release_buffer(LPVBOXCOMM vb)
{
	if (vb->vds && vds_available()) {
		int err = vds_unlock_dma_buffer_region(&vb->dds, 0);
		if (err) {
			dlog_print("Error while VDS unlocking, err=");
			dlog_printd(err);
			dlog_endline();
			// Ignore the error, it's not like we can do anything
		}
	}
	vb->vds = false;
	vb->dds.regionSize = 0;
	vb->dds.segOrSelector = 0;
	vb->dds.offset = 0;
	vb->dds.bufferId = 0;
	vb->dds.physicalAddress = 0;

	return 0;
}