summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libcpu/powerpc/new-exceptions/bspsupport/ppc_exc_prologue.c
blob: b259589aa62347ed2381fad95faea03290785f6f (plain) (blame)
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
/**
 * @file
 *
 * @ingroup ppc_exc
 *
 * @brief PowerPC Exceptions implementation.
 */

/*
 * Copyright (C) 2007 Till Straumann <strauman@slac.stanford.edu>
 *
 * Copyright (C) 2009 embedded brains GmbH.                      
 *
 * The license and distribution terms for this file may be
 * found in found in the file LICENSE in this distribution or at
 * http://www.rtems.com/license/LICENSE.
 *
 * $Id$
 */

#include <string.h>

#include <bsp/vectors.h>

/* Offset into minimal prolog where vector number is hardcoded */
#define PPC_EXC_PROLOG_VEC_OFFSET 2

/* Symbols are defined by the linker */
extern const char ppc_exc_min_prolog_size [];
extern const char ppc_exc_tgpr_clr_prolog_size [];

/* Special prologue for handling register shadowing on 603-style CPUs */
extern const uint32_t ppc_exc_tgpr_clr_prolog [];

/*
 * Classic prologue which determines the vector dynamically from the offset
 * address. This must only be used for classic, synchronous exceptions with a
 * vector offset aligned on a 256-byte boundary.
 */
extern const uint32_t ppc_exc_min_prolog_auto [];

extern const uint32_t ppc_exc_min_prolog_auto_packed [];

/* Minimal prologue templates */
extern const uint32_t ppc_exc_min_prolog_async_tmpl_std [];
extern const uint32_t ppc_exc_min_prolog_sync_tmpl_std [];
extern const uint32_t ppc_exc_min_prolog_async_tmpl_p405_crit [];
extern const uint32_t ppc_exc_min_prolog_sync_tmpl_p405_crit [];
extern const uint32_t ppc_exc_min_prolog_async_tmpl_bookE_crit [];
extern const uint32_t ppc_exc_min_prolog_sync_tmpl_bookE_crit [];
extern const uint32_t ppc_exc_min_prolog_sync_tmpl_e500_mchk [];
extern const uint32_t ppc_exc_min_prolog_async_tmpl_e500_mchk [];
extern const uint32_t ppc_exc_min_prolog_tmpl_naked [];

static const uint32_t *const ppc_exc_prologue_templates [] = {
  [PPC_EXC_CLASSIC] = ppc_exc_min_prolog_sync_tmpl_std,
  [PPC_EXC_CLASSIC_ASYNC] = ppc_exc_min_prolog_async_tmpl_std,
  [PPC_EXC_405_CRITICAL] = ppc_exc_min_prolog_sync_tmpl_p405_crit,
  [PPC_EXC_405_CRITICAL_ASYNC] = ppc_exc_min_prolog_async_tmpl_p405_crit,
  [PPC_EXC_BOOKE_CRITICAL] = ppc_exc_min_prolog_sync_tmpl_bookE_crit,
  [PPC_EXC_BOOKE_CRITICAL_ASYNC] = ppc_exc_min_prolog_async_tmpl_bookE_crit,
  [PPC_EXC_E500_MACHCHK] = ppc_exc_min_prolog_sync_tmpl_e500_mchk,
  [PPC_EXC_E500_MACHCHK_ASYNC] = ppc_exc_min_prolog_async_tmpl_e500_mchk,
  [PPC_EXC_NAKED] = ppc_exc_min_prolog_tmpl_naked
};

rtems_status_code ppc_exc_make_prologue(
  unsigned vector,
  ppc_exc_category category,
  uint32_t *prologue,
  size_t *prologue_size
)
{
  const uint32_t *prologue_template = NULL;
  size_t prologue_template_size = 0;
  uintptr_t vector_address = (uintptr_t) ppc_exc_vector_address(vector);
  bool fixup_vector = false;

  if (!ppc_exc_is_valid_category(category)) {
    return RTEMS_INVALID_NUMBER;
  }

  if (
    ppc_cpu_has_shadowed_gprs()
      && (vector == ASM_60X_IMISS_VECTOR
        || vector == ASM_60X_DLMISS_VECTOR
        || vector == ASM_60X_DSMISS_VECTOR)
  ) {
    prologue_template = ppc_exc_tgpr_clr_prolog;
    prologue_template_size = (size_t) ppc_exc_tgpr_clr_prolog_size;
  } else if (
    category == PPC_EXC_CLASSIC
      && ((vector_address & 0xffU) == 0
        || (ppc_cpu_has_ivpr_and_ivor() && (vector_address & 0xfU) == 0))
  ) {
    if (ppc_cpu_has_ivpr_and_ivor()) {
      prologue_template = ppc_exc_min_prolog_auto_packed;
    } else {
      prologue_template = ppc_exc_min_prolog_auto;
    }
    prologue_template_size = (size_t) ppc_exc_min_prolog_size;
  } else {
    prologue_template = ppc_exc_prologue_templates [category];
    prologue_template_size = (size_t) ppc_exc_min_prolog_size;
    fixup_vector = true;
  }

  if (prologue_template_size <= *prologue_size) {
    *prologue_size = prologue_template_size;

    memcpy(prologue, prologue_template, prologue_template_size);

    if (fixup_vector) {
      if (vector <= 0x7fffU) {
        prologue [PPC_EXC_PROLOG_VEC_OFFSET] =
          (prologue [PPC_EXC_PROLOG_VEC_OFFSET] & 0xffff8000U)
            | (vector & 0x7fffU);
      } else {
        return RTEMS_INVALID_ID;
      }
    }
  } else {
    return RTEMS_INVALID_SIZE;
  }

  return RTEMS_SUCCESSFUL;
}