Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/iommu/iommu_gas.c
Show All 25 Lines | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | * 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 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||
* SUCH DAMAGE. | * SUCH DAMAGE. | ||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#define RB_AUGMENT(entry) iommu_gas_augment_entry(entry) | #define RB_AUGMENT_CHECK(entry) iommu_gas_augment_entry(entry) | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/bus.h> | #include <sys/bus.h> | ||||
#include <sys/interrupt.h> | #include <sys/interrupt.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/ktr.h> | #include <sys/ktr.h> | ||||
▲ Show 20 Lines • Show All 91 Lines • ▼ Show 20 Lines | iommu_gas_cmp_entries(struct iommu_map_entry *a, struct iommu_map_entry *b) | ||||
if (a->end < b->end) | if (a->end < b->end) | ||||
return (-1); | return (-1); | ||||
else if (b->end < a->end) | else if (b->end < a->end) | ||||
return (1); | return (1); | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | /* | ||||
* Update augmentation data based on data from children. | |||||
* Return true if and only if the update changes the augmentation data. | |||||
*/ | |||||
static bool | |||||
iommu_gas_augment_entry(struct iommu_map_entry *entry) | iommu_gas_augment_entry(struct iommu_map_entry *entry) | ||||
{ | { | ||||
struct iommu_map_entry *child; | struct iommu_map_entry *child; | ||||
iommu_gaddr_t free_down; | iommu_gaddr_t bound, delta, free_down; | ||||
free_down = 0; | free_down = 0; | ||||
bound = entry->start; | |||||
if ((child = RB_LEFT(entry, rb_entry)) != NULL) { | if ((child = RB_LEFT(entry, rb_entry)) != NULL) { | ||||
free_down = MAX(free_down, child->free_down); | free_down = MAX(child->free_down, bound - child->last); | ||||
free_down = MAX(free_down, entry->start - child->last); | bound = child->first; | ||||
entry->first = child->first; | } | ||||
} else | delta = bound - entry->first; | ||||
entry->first = entry->start; | entry->first = bound; | ||||
bound = entry->end; | |||||
if ((child = RB_RIGHT(entry, rb_entry)) != NULL) { | if ((child = RB_RIGHT(entry, rb_entry)) != NULL) { | ||||
free_down = MAX(free_down, child->free_down); | free_down = MAX(free_down, child->free_down); | ||||
free_down = MAX(free_down, child->first - entry->end); | free_down = MAX(free_down, child->first - bound); | ||||
entry->last = child->last; | bound = child->last; | ||||
} else | } | ||||
entry->last = entry->end; | delta += entry->last - bound; | ||||
if (delta == 0) | |||||
delta = entry->free_down - free_down; | |||||
entry->last = bound; | |||||
entry->free_down = free_down; | entry->free_down = free_down; | ||||
/* | |||||
* Return true either if the value of last-first changed, | |||||
* or if free_down changed. | |||||
*/ | |||||
return (delta != 0); | |||||
} | } | ||||
RB_GENERATE(iommu_gas_entries_tree, iommu_map_entry, rb_entry, | RB_GENERATE(iommu_gas_entries_tree, iommu_map_entry, rb_entry, | ||||
iommu_gas_cmp_entries); | iommu_gas_cmp_entries); | ||||
#ifdef INVARIANTS | #ifdef INVARIANTS | ||||
static void | static void | ||||
iommu_gas_check_free(struct iommu_domain *domain) | iommu_gas_check_free(struct iommu_domain *domain) | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | iommu_gas_init_domain(struct iommu_domain *domain) | ||||
begin = iommu_gas_alloc_entry(domain, IOMMU_PGF_WAITOK); | begin = iommu_gas_alloc_entry(domain, IOMMU_PGF_WAITOK); | ||||
end = iommu_gas_alloc_entry(domain, IOMMU_PGF_WAITOK); | end = iommu_gas_alloc_entry(domain, IOMMU_PGF_WAITOK); | ||||
IOMMU_DOMAIN_LOCK(domain); | IOMMU_DOMAIN_LOCK(domain); | ||||
KASSERT(domain->entries_cnt == 2, ("dirty domain %p", domain)); | KASSERT(domain->entries_cnt == 2, ("dirty domain %p", domain)); | ||||
KASSERT(RB_EMPTY(&domain->rb_root), | KASSERT(RB_EMPTY(&domain->rb_root), | ||||
("non-empty entries %p", domain)); | ("non-empty entries %p", domain)); | ||||
begin->start = 0; | /* | ||||
begin->end = IOMMU_PAGE_SIZE; | * The end entry must be inserted first because it has a zero-length gap | ||||
begin->flags = IOMMU_MAP_ENTRY_PLACE | IOMMU_MAP_ENTRY_UNMAPPED; | * between start and end. Initially, all augmentation data for a new | ||||
iommu_gas_rb_insert(domain, begin); | * entry is zero. Function iommu_gas_augment_entry will compute no | ||||
* change in the value of (start-end) and no change in the value of | |||||
* free_down, so it will return false to suggest that nothing changed in | |||||
* the entry. Thus, inserting the end entry second prevents | |||||
* augmentation information to be propogated to the begin entry at the | |||||
* tree root. So it is inserted first. | |||||
*/ | |||||
end->start = domain->end; | end->start = domain->end; | ||||
end->end = domain->end; | end->end = domain->end; | ||||
end->flags = IOMMU_MAP_ENTRY_PLACE | IOMMU_MAP_ENTRY_UNMAPPED; | end->flags = IOMMU_MAP_ENTRY_PLACE | IOMMU_MAP_ENTRY_UNMAPPED; | ||||
iommu_gas_rb_insert(domain, end); | iommu_gas_rb_insert(domain, end); | ||||
begin->start = 0; | |||||
begin->end = IOMMU_PAGE_SIZE; | |||||
begin->flags = IOMMU_MAP_ENTRY_PLACE | IOMMU_MAP_ENTRY_UNMAPPED; | |||||
iommu_gas_rb_insert(domain, begin); | |||||
domain->first_place = begin; | domain->first_place = begin; | ||||
domain->last_place = end; | domain->last_place = end; | ||||
domain->flags |= IOMMU_DOMAIN_GAS_INITED; | domain->flags |= IOMMU_DOMAIN_GAS_INITED; | ||||
IOMMU_DOMAIN_UNLOCK(domain); | IOMMU_DOMAIN_UNLOCK(domain); | ||||
} | } | ||||
void | void | ||||
▲ Show 20 Lines • Show All 742 Lines • Show Last 20 Lines |