1/**
2 * \file
3 * \brief VT-d domain management
4 */
5
6/*
7 * Copyright (c) 2014, University of Washington.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, CAB F.78, Universitaetstr. 6, CH-8092 Zurich.
13 * Attn: Systems Group.
14 */
15
16#ifndef VTD_DOMAINS_H
17#define VTD_DOMAINS_H
18
19struct vtd_unit;
20
21struct vtd_domain {
22    // x86-64 VNode capability for the domain pagetable structure
23    struct capref   pml4;
24    // physical address associated with capability
25    genpaddr_t      pt_gp;
26    uint16_t        did;
27    struct vtd_domain *  prev;
28    struct vtd_domain *  next;
29    // Pointer to the list of remapping hardware units
30    struct vtd_unit *    units;
31};
32
33// A domain list is a sorted and bounded doubly-linked list with
34// min_did and max_did specifying the bounds
35struct vtd_domain_list {
36    struct vtd_domain *head;
37    struct vtd_domain *tail;
38    int min_did;
39    int max_did;
40};
41
42// Returns true if the domain list is empty, else false.
43static inline int domain_list_empty(struct vtd_domain_list *lst)
44{
45    return (lst->head == NULL);
46}
47
48static inline struct vtd_domain_list *vtd_new_domain_list(void)
49{
50    struct vtd_domain_list *new_list;
51    new_list = (struct vtd_domain_list *)malloc(sizeof(struct vtd_domain_list));
52    assert(new_list != NULL);
53    new_list->head = NULL;
54    new_list->tail = NULL;
55    new_list->min_did = 0;
56    new_list->max_did = 0;
57    return new_list;
58}
59
60static inline struct vtd_domain *vtd_new_domain(int did, genpaddr_t pt, struct capref pml4, struct vtd_unit *units)
61{
62    struct vtd_domain *new_domain = (struct vtd_domain *)malloc(sizeof(struct vtd_domain));
63    assert(new_domain != NULL);
64    new_domain->did   = did;
65    new_domain->pt_gp = pt;
66    new_domain->pml4  = pml4;
67    new_domain->next  = NULL;
68    new_domain->prev  = NULL;
69    new_domain->units = units;
70    return new_domain;
71}
72
73// Inserts newd as the new head of lst, a domain list with a size > 1
74static inline void vtd_insert_new_head(struct vtd_domain *newd, struct vtd_domain_list *lst)
75{
76    newd->next = lst->head;
77    lst->head->prev = newd;
78    lst->head = newd;
79}
80
81// Inserts newd as the new tail of lst, a domain list with a size > 1
82static inline void vtd_insert_new_tail(struct vtd_domain_list *lst, struct vtd_domain *newd)
83{
84    lst->tail->next = newd;
85    newd->prev = lst->tail;
86    lst->tail = newd;
87}
88
89// Inserts the domain newd between prevd and nextd.
90static inline void vtd_insert_between(struct vtd_domain *prevd, struct vtd_domain *newd, struct vtd_domain *nextd)
91{
92    newd->next = nextd;
93    newd->prev = prevd;
94    prevd->next = newd;
95    nextd->prev = newd;
96}
97
98// Inserts the domain newd before the domain nextd in the domain list doms.
99static inline void vtd_insert_domain(struct vtd_domain *newd, struct vtd_domain *nextd, struct vtd_domain_list *doms) {
100    assert(newd != NULL);
101    if (domain_list_empty(doms)) {           // empty
102        doms->head = newd;
103	doms->tail = newd;
104    } else if (newd->did == doms->min_did) { // new head
105        vtd_insert_new_head(newd, doms);
106    } else if (nextd == NULL) {              // new tail
107        vtd_insert_new_tail(doms, newd);
108    } else {                                 // between
109        vtd_insert_between(nextd->prev, newd, nextd);
110    }
111}
112
113// Deletes the head of lst, a domain list with a size > 1.
114static inline void vtd_delete_head(struct vtd_domain_list *lst)
115{
116    struct vtd_domain *old_head = lst->head;
117    lst->head->next->prev = NULL;
118    lst->head = lst->head->next;
119    free(old_head);
120}
121
122// Deletes the tail of lst, a domain list with a size > 1.
123static inline void vtd_delete_tail(struct vtd_domain_list *lst)
124{
125    struct vtd_domain *old_tail = lst->tail;
126    lst->tail->prev->next = NULL;
127    lst->tail = lst->tail->prev;
128    free(old_tail);
129}
130
131// Deletes the domain d between the domains prevd and nextd.
132static inline void vtd_delete_between(struct vtd_domain *prevd, struct vtd_domain *d, struct vtd_domain *nextd)
133{
134    struct vtd_domain *temp = d;
135    prevd->next = nextd;
136    nextd->prev = prevd;
137    free(temp);
138}
139
140// Deletes a list of domains consisting of just a single element.
141static inline void vtd_delete_single(struct vtd_domain_list *lst) {
142    free(lst->head);
143    lst->head = NULL;
144    lst->tail = NULL;
145}
146
147// Deletes the domain d from the non-empty list of domains lst.
148static inline void vtd_delete_domain(struct vtd_domain *d, struct vtd_domain_list *lst)
149{
150    assert(d != NULL);
151    assert(lst != NULL);
152    if (lst->head == lst->tail) {
153	vtd_delete_single(lst);
154    } else if (d == lst->head) {
155        vtd_delete_head(lst);
156    } else if (d == lst->tail) {
157        vtd_delete_tail(lst);
158    } else {
159        vtd_delete_between(d->prev, d, d->next);
160    }
161}
162
163#endif //VTD_DOMAINS_H
164