1fec0fc0aSEric Blake /* 2fec0fc0aSEric Blake * QEMU 64-bit address ranges 3fec0fc0aSEric Blake * 4fec0fc0aSEric Blake * Copyright (c) 2015-2016 Red Hat, Inc. 5fec0fc0aSEric Blake * 6e361a772SThomas Huth * This program is free software; you can redistribute it and/or 7fec0fc0aSEric Blake * modify it under the terms of the GNU General Public 8fec0fc0aSEric Blake * License as published by the Free Software Foundation; either 9fec0fc0aSEric Blake * version 2 of the License, or (at your option) any later version. 10fec0fc0aSEric Blake * 11e361a772SThomas Huth * This program is distributed in the hope that it will be useful, 12fec0fc0aSEric Blake * but WITHOUT ANY WARRANTY; without even the implied warranty of 13fec0fc0aSEric Blake * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14e361a772SThomas Huth * General Public License for more details. 15fec0fc0aSEric Blake * 16e361a772SThomas Huth * You should have received a copy of the GNU General Public License 17e361a772SThomas Huth * along with this program; if not, see <http://www.gnu.org/licenses/>. 18fec0fc0aSEric Blake */ 19fec0fc0aSEric Blake 20fec0fc0aSEric Blake #include "qemu/osdep.h" 21fec0fc0aSEric Blake #include "qemu/range.h" 22fec0fc0aSEric Blake 2343f04cbeSEric Auger int range_compare(Range *a, Range *b) 24fec0fc0aSEric Blake { 256dd726a2SMarkus Armbruster assert(!range_is_empty(a) && !range_is_empty(b)); 266dd726a2SMarkus Armbruster 276dd726a2SMarkus Armbruster /* Careful, avoid wraparound */ 286dd726a2SMarkus Armbruster if (b->lob && b->lob - 1 > a->upb) { 297c47959dSEric Blake return -1; 30db486cc3SEric Blake } 316dd726a2SMarkus Armbruster if (a->lob && a->lob - 1 > b->upb) { 327c47959dSEric Blake return 1; 337c47959dSEric Blake } 34db486cc3SEric Blake return 0; 357c47959dSEric Blake } 367c47959dSEric Blake 37db486cc3SEric Blake /* Insert @data into @list of ranges; caller no longer owns @data */ 387c47959dSEric Blake GList *range_list_insert(GList *list, Range *data) 39fec0fc0aSEric Blake { 40db486cc3SEric Blake GList *l; 41fec0fc0aSEric Blake 42a0efbf16SMarkus Armbruster assert(!range_is_empty(data)); 43db486cc3SEric Blake 44db486cc3SEric Blake /* Skip all list elements strictly less than data */ 45db486cc3SEric Blake for (l = list; l && range_compare(l->data, data) < 0; l = l->next) { 46fec0fc0aSEric Blake } 47fec0fc0aSEric Blake 48db486cc3SEric Blake if (!l || range_compare(l->data, data) > 0) { 49db486cc3SEric Blake /* Rest of the list (if any) is strictly greater than @data */ 50db486cc3SEric Blake return g_list_insert_before(list, l, data); 51fec0fc0aSEric Blake } 52fec0fc0aSEric Blake 53db486cc3SEric Blake /* Current list element overlaps @data, merge the two */ 54db486cc3SEric Blake range_extend(l->data, data); 55db486cc3SEric Blake g_free(data); 56db486cc3SEric Blake 57db486cc3SEric Blake /* Merge any subsequent list elements that now also overlap */ 58db486cc3SEric Blake while (l->next && range_compare(l->data, l->next->data) == 0) { 59db486cc3SEric Blake GList *new_l; 60db486cc3SEric Blake 61db486cc3SEric Blake range_extend(l->data, l->next->data); 62db486cc3SEric Blake g_free(l->next->data); 63db486cc3SEric Blake new_l = g_list_delete_link(list, l->next); 64db486cc3SEric Blake assert(new_l == list); 65fec0fc0aSEric Blake } 66fec0fc0aSEric Blake 67fec0fc0aSEric Blake return list; 68fec0fc0aSEric Blake } 69*b439595aSEric Auger 70*b439595aSEric Auger static inline 71*b439595aSEric Auger GList *append_new_range(GList *list, uint64_t lob, uint64_t upb) 72*b439595aSEric Auger { 73*b439595aSEric Auger Range *new = g_new0(Range, 1); 74*b439595aSEric Auger 75*b439595aSEric Auger range_set_bounds(new, lob, upb); 76*b439595aSEric Auger return g_list_append(list, new); 77*b439595aSEric Auger } 78*b439595aSEric Auger 79*b439595aSEric Auger 80*b439595aSEric Auger void range_inverse_array(GList *in, GList **rev, 81*b439595aSEric Auger uint64_t low, uint64_t high) 82*b439595aSEric Auger { 83*b439595aSEric Auger Range *r, *rn; 84*b439595aSEric Auger GList *l = in, *out = *rev; 85*b439595aSEric Auger 86*b439595aSEric Auger for (l = in; l && range_upb(l->data) < low; l = l->next) { 87*b439595aSEric Auger continue; 88*b439595aSEric Auger } 89*b439595aSEric Auger 90*b439595aSEric Auger if (!l) { 91*b439595aSEric Auger out = append_new_range(out, low, high); 92*b439595aSEric Auger goto exit; 93*b439595aSEric Auger } 94*b439595aSEric Auger r = (Range *)l->data; 95*b439595aSEric Auger 96*b439595aSEric Auger /* first range lob is greater than min, insert a first range */ 97*b439595aSEric Auger if (range_lob(r) > low) { 98*b439595aSEric Auger out = append_new_range(out, low, MIN(range_lob(r) - 1, high)); 99*b439595aSEric Auger } 100*b439595aSEric Auger 101*b439595aSEric Auger /* insert a range inbetween each original range until we reach high */ 102*b439595aSEric Auger for (; l->next; l = l->next) { 103*b439595aSEric Auger r = (Range *)l->data; 104*b439595aSEric Auger rn = (Range *)l->next->data; 105*b439595aSEric Auger if (range_lob(r) >= high) { 106*b439595aSEric Auger goto exit; 107*b439595aSEric Auger } 108*b439595aSEric Auger if (range_compare(r, rn)) { 109*b439595aSEric Auger out = append_new_range(out, range_upb(r) + 1, 110*b439595aSEric Auger MIN(range_lob(rn) - 1, high)); 111*b439595aSEric Auger } 112*b439595aSEric Auger } 113*b439595aSEric Auger 114*b439595aSEric Auger /* last range */ 115*b439595aSEric Auger r = (Range *)l->data; 116*b439595aSEric Auger 117*b439595aSEric Auger /* last range upb is less than max, insert a last range */ 118*b439595aSEric Auger if (range_upb(r) < high) { 119*b439595aSEric Auger out = append_new_range(out, range_upb(r) + 1, high); 120*b439595aSEric Auger } 121*b439595aSEric Auger exit: 122*b439595aSEric Auger *rev = out; 123*b439595aSEric Auger } 124