471
472
/*
473
* This is returned by get_l2_iste() and has everything we need to do
474
- * the writeback of the L2 ISTE word in put_l2_iste(). Currently the
475
- * get/put functions always directly do guest memory reads and writes
476
- * to update the L2 ISTE. In a future commit we will add support for a
477
- * cache of some of the ISTE data in a local hashtable; the APIs are
478
- * designed with that in mind.
474
+ * the writeback of the L2 ISTE word in put_l2_iste(). Not all these
475
+ * fields are always valid; they are private to the implementation of
476
+ * get_l2_iste() and put_l2_iste().
477
*/
478
typedef struct L2_ISTE_Handle {
479
+ /* Guest memory address of the L2 ISTE; valid only if !hashed */
480
hwaddr l2_iste_addr;
482
- uint32_t l2_iste;
481
+ union {
482
+ /* Actual L2_ISTE word; valid only if !hashed */
483
+ uint32_t l2_iste;
484
+ /* Pointer to L2 ISTE word; valid only if hashed */
485
+ uint32_t *l2_iste_p;
486
+ };
487
+ uint32_t id;
488
+ /* True if this ISTE is currently in the cache */
489
+ bool hashed;
490
} L2_ISTE_Handle;
491
492
static uint32_t *get_l2_iste(GICv5Common *cs, const GICv5ISTConfig *cfg,
506
* If the ISTE could not be read (typically because of a memory
507
* error), return NULL.
508
*/
509
+ uint32_t *hashvalue;
510
+
511
+ if (!cfg->valid) {
512
+ /* Catch invalid config early, it has no lpi_cache */
513
+ return NULL;
514
+ }
515
+
516
+ hashvalue = g_hash_table_lookup(cfg->lpi_cache,
517
+ GINT_TO_POINTER(id));
518
+
519
+ h->id = id;
520
+
521
+ if (hashvalue) {
522
+ h->hashed = true;
523
+ h->l2_iste_p = hashvalue;
524
+ return hashvalue;
525
+ }
526
+
527
+ h->hashed = false;
528
if (!get_l2_iste_addr(cs, cfg, id, &h->l2_iste_addr) ||
529
!read_l2_iste_mem(cs, cfg, h->l2_iste_addr, &h->l2_iste)) {
530
return NULL;
540
* Once this has been called the L2_ISTE_Handle @h and the pointer
541
* to the L2 ISTE word are no longer valid.
542
*/
543
+ if (h->hashed) {
544
+ uint32_t l2_iste = *h->l2_iste_p;
545
+ if (!FIELD_EX32(l2_iste, L2_ISTE, PENDING)) {
546
+ /*
547
+ * We just made this not pending: remove from hash table
548
+ * and write back to memory.
549
+ */
550
+ hwaddr l2_iste_addr;
551
+
552
+ g_hash_table_remove(cfg->lpi_cache, GINT_TO_POINTER(h->id));
553
+ if (get_l2_iste_addr(cs, cfg, h->id, &l2_iste_addr)) {
554
+ write_l2_iste_mem(cs, cfg, l2_iste_addr, l2_iste);
555
+ /* Writeback errors are ignored. */
556
+ }
557
+ }
558
+ return;
559
+ }
560
+
561
+ if (FIELD_EX32(h->l2_iste, L2_ISTE, PENDING)) {
562
+ /*
563
+ * We just made this pending: add it to the hash table, and
564
+ * don't bother writing it back to memory.
565
+ */
566
+ uint32_t *hashvalue = g_new(uint32_t, 1);
567
+ *hashvalue = h->l2_iste;
568
+ g_hash_table_insert(cfg->lpi_cache, GINT_TO_POINTER(h->id), hashvalue);
569
+ return;
570
+ }
571
write_l2_iste_mem(cs, cfg, h->l2_iste_addr, h->l2_iste);
572
}
573
950
"physical address 0x" HWADDR_FMT_plx "\n", intid, l1_addr);
951
}
952
953
+/* Data we need to pass through to irs_clean_lpi_cache_entry() */
954
+typedef struct CleanLPICacheUserData {
955
+ GICv5Common *cs;
956
+ GICv5ISTConfig *cfg;
957
+} CleanLPICacheUserData;
958
+
959
+static gboolean irs_clean_lpi_cache_entry(gpointer key, gpointer value,
960
+ gpointer user_data)
961
+{
962
+ /* Drop this entry from the LPI cache, writing it back to guest memory. */
963
+ CleanLPICacheUserData *ud = user_data;
964
+ hwaddr l2_iste_addr;
965
+ uint64_t id = GPOINTER_TO_INT(key);
966
+ uint32_t l2_iste = *(uint32_t *)value;
967
+
968
+ if (!get_l2_iste_addr(ud->cs, ud->cfg, id, &l2_iste_addr) ||
969
+ !write_l2_iste_mem(ud->cs, ud->cfg, l2_iste_addr, l2_iste)) {
970
+ /* We drop the cached entry regardless of writeback errors */
971
+ return true;
972
+ }
973
+ return true;
974
+}
975
+
976
+static void irs_clean_lpi_cache(GICv5Common *cs, GICv5ISTConfig *cfg)
977
+{
978
+ /* Write everything in the LPI cache out to guest memory */
979
+ CleanLPICacheUserData ud;
980
+ ud.cs = cs;
981
+ ud.cfg = cfg;
982
+
983
+ g_hash_table_foreach_remove(cfg->lpi_cache, irs_clean_lpi_cache_entry, &ud);
984
+}
985
+
986
static void irs_ist_baser_write(GICv5 *s, GICv5Domain domain, uint64_t value)
987
{
988
GICv5Common *cs = ARM_GICV5_COMMON(s);
994
/* Ignore 1->1 transition */
995
return;
996
}
997
+ irs_clean_lpi_cache(cs, &s->phys_lpi_config[domain]);
998
cs->irs_ist_baser[domain] = FIELD_DP64(cs->irs_ist_baser[domain],
999
IRS_IST_BASER, VALID, valid);
1000
s->phys_lpi_config[domain].valid = false;
1056
cfg->l2_idx_bits = l2_idx_bits;
1057
cfg->structure = FIELD_EX64(cs->irs_ist_cfgr[domain],
1058
IRS_IST_CFGR, STRUCTURE);
1059
+ if (!cfg->lpi_cache) {
1060
+ /*
1061
+ * Keys are GINT_TO_POINTER(intid), so we want the g_direct_hash
1062
+ * and g_direct_equal hash and equality functions. We don't
1063
+ * want to free the keys, but we do want to free the values
1064
+ * (which are pointer-to-uint32_t).
1065
+ */
1066
+ cfg->lpi_cache = g_hash_table_new_full(NULL, NULL, NULL, g_free);
1067
+ }
1068
cfg->valid = true;
1069
trace_gicv5_ist_valid(domain_name[domain], cfg->base, cfg->id_bits,
1070
cfg->l2_idx_bits, cfg->istsz, cfg->structure);
1525
/* IRS_IST_BASER and IRS_IST_CFGR reset to 0, clear cached info */
1526
for (int i = 0; i < NUM_GICV5_DOMAINS; i++) {
1527
s->phys_lpi_config[i].valid = false;
1528
+ /*
1529
+ * If we got reset (power-cycled) with data in the cache, don't
1530
+ * write it out to guest memory; just return to "empty cache".
1531
+ */
1532
+ if (s->phys_lpi_config[i].lpi_cache) {
1533
+ g_hash_table_remove_all(s->phys_lpi_config[i].lpi_cache);
1534
+ }
1535
}
1536
}
1537