summaryrefslogtreecommitdiffstats
path: root/NetworkPkg/SnpDxe/Station_address.c
blob: 66ddbf6831148c21c9647f98b9d8a3d67ae6141e (plain)
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
/** @file
    Implementation of reading the MAC address of a network adapter.

Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "Snp.h"

/**
  Call UNDI to read the MAC address of the NIC and update the mode structure
  with the address.

  @param  Snp         Pointer to snp driver structure.

  @retval EFI_SUCCESS       The MAC address of the NIC is read successfully.
  @retval EFI_DEVICE_ERROR  Failed to read the MAC address of the NIC.

**/
EFI_STATUS
PxeGetStnAddr (
  SNP_DRIVER  *Snp
  )
{
  PXE_DB_STATION_ADDRESS  *Db;

  Db               = Snp->Db;
  Snp->Cdb.OpCode  = PXE_OPCODE_STATION_ADDRESS;
  Snp->Cdb.OpFlags = PXE_OPFLAGS_STATION_ADDRESS_READ;

  Snp->Cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
  Snp->Cdb.CPBsize = PXE_CPBSIZE_NOT_USED;

  Snp->Cdb.DBsize = (UINT16)sizeof (PXE_DB_STATION_ADDRESS);
  Snp->Cdb.DBaddr = (UINT64)(UINTN)Db;

  Snp->Cdb.StatCode  = PXE_STATCODE_INITIALIZE;
  Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
  Snp->Cdb.IFnum     = Snp->IfNum;
  Snp->Cdb.Control   = PXE_CONTROL_LAST_CDB_IN_LIST;

  //
  // Issue UNDI command and check result.
  //
  DEBUG ((DEBUG_NET, "\nsnp->undi.station_addr()  "));

  (*Snp->IssueUndi32Command)((UINT64)(UINTN)&Snp->Cdb);

  if (Snp->Cdb.StatCode != PXE_STATCODE_SUCCESS) {
    DEBUG (
      (DEBUG_ERROR,
       "\nsnp->undi.station_addr()  %xh:%xh\n",
       Snp->Cdb.StatFlags,
       Snp->Cdb.StatCode)
      );

    return EFI_DEVICE_ERROR;
  }

  //
  // Set new station address in SNP->Mode structure and return success.
  //
  CopyMem (
    &(Snp->Mode.CurrentAddress),
    &Db->StationAddr,
    Snp->Mode.HwAddressSize
    );

  CopyMem (
    &Snp->Mode.BroadcastAddress,
    &Db->BroadcastAddr,
    Snp->Mode.HwAddressSize
    );

  CopyMem (
    &Snp->Mode.PermanentAddress,
    &Db->PermanentAddr,
    Snp->Mode.HwAddressSize
    );

  return EFI_SUCCESS;
}

/**
  Call UNDI to set a new MAC address for the NIC.

  @param  Snp         Pointer to Snp driver structure.
  @param  NewMacAddr  Pointer to a MAC address to be set for the NIC, if this is
                      NULL then this routine resets the mac address to the NIC's
                      original address.


**/
EFI_STATUS
PxeSetStnAddr (
  SNP_DRIVER       *Snp,
  EFI_MAC_ADDRESS  *NewMacAddr
  )
{
  PXE_CPB_STATION_ADDRESS  *Cpb;
  PXE_DB_STATION_ADDRESS   *Db;

  Cpb             = Snp->Cpb;
  Db              = Snp->Db;
  Snp->Cdb.OpCode = PXE_OPCODE_STATION_ADDRESS;

  if (NewMacAddr == NULL) {
    Snp->Cdb.OpFlags = PXE_OPFLAGS_STATION_ADDRESS_RESET;
    Snp->Cdb.CPBsize = PXE_CPBSIZE_NOT_USED;
    Snp->Cdb.CPBaddr = PXE_CPBADDR_NOT_USED;
  } else {
    Snp->Cdb.OpFlags = PXE_OPFLAGS_STATION_ADDRESS_WRITE;
    //
    // Supplying a new address in the CPB will make undi change the mac address to the new one.
    //
    CopyMem (&Cpb->StationAddr, NewMacAddr, Snp->Mode.HwAddressSize);

    Snp->Cdb.CPBsize = (UINT16)sizeof (PXE_CPB_STATION_ADDRESS);
    Snp->Cdb.CPBaddr = (UINT64)(UINTN)Cpb;
  }

  Snp->Cdb.DBsize = (UINT16)sizeof (PXE_DB_STATION_ADDRESS);
  Snp->Cdb.DBaddr = (UINT64)(UINTN)Db;

  Snp->Cdb.StatCode  = PXE_STATCODE_INITIALIZE;
  Snp->Cdb.StatFlags = PXE_STATFLAGS_INITIALIZE;
  Snp->Cdb.IFnum     = Snp->IfNum;
  Snp->Cdb.Control   = PXE_CONTROL_LAST_CDB_IN_LIST;

  //
  // Issue UNDI command and check result.
  //
  DEBUG ((DEBUG_NET, "\nsnp->undi.station_addr()  "));

  (*Snp->IssueUndi32Command)((UINT64)(UINTN)&Snp->Cdb);

  if (Snp->Cdb.StatCode != PXE_STATCODE_SUCCESS) {
    DEBUG (
      (DEBUG_ERROR,
       "\nsnp->undi.station_addr()  %xh:%xh\n",
       Snp->Cdb.StatFlags,
       Snp->Cdb.StatCode)
      );

    //
    // UNDI command failed.  Return UNDI status to caller.
    //
    return EFI_DEVICE_ERROR;
  }

  //
  // read the changed address and save it in SNP->Mode structure
  //
  PxeGetStnAddr (Snp);

  return EFI_SUCCESS;
}

/**
  Modifies or resets the current station address, if supported.

  This function modifies or resets the current station address of a network
  interface, if supported. If Reset is TRUE, then the current station address is
  set to the network interface's permanent address. If Reset is FALSE, and the
  network interface allows its station address to be modified, then the current
  station address is changed to the address specified by New. If the network
  interface does not allow its station address to be modified, then
  EFI_INVALID_PARAMETER will be returned. If the station address is successfully
  updated on the network interface, EFI_SUCCESS will be returned. If the driver
  has not been initialized, EFI_DEVICE_ERROR will be returned.

  @param This  A pointer to the EFI_SIMPLE_NETWORK_PROTOCOL instance.
  @param Reset Flag used to reset the station address to the network interface's
               permanent address.
  @param New   New station address to be used for the network interface.


  @retval EFI_SUCCESS           The network interface's station address was updated.
  @retval EFI_NOT_STARTED       The Simple Network Protocol interface has not been
                                started by calling Start().
  @retval EFI_INVALID_PARAMETER The New station address was not accepted by the NIC.
  @retval EFI_INVALID_PARAMETER Reset is FALSE and New is NULL.
  @retval EFI_DEVICE_ERROR      The Simple Network Protocol interface has not
                                been initialized by calling Initialize().
  @retval EFI_DEVICE_ERROR      An error occurred attempting to set the new
                                station address.
  @retval EFI_UNSUPPORTED       The NIC does not support changing the network
                                interface's station address.

**/
EFI_STATUS
EFIAPI
SnpUndi32StationAddress (
  IN EFI_SIMPLE_NETWORK_PROTOCOL  *This,
  IN BOOLEAN                      Reset,
  IN EFI_MAC_ADDRESS              *New OPTIONAL
  )
{
  SNP_DRIVER  *Snp;
  EFI_STATUS  Status;
  EFI_TPL     OldTpl;

  //
  // Check for invalid parameter combinations.
  //
  if ((This == NULL) ||
      (!Reset && (New == NULL)))
  {
    return EFI_INVALID_PARAMETER;
  }

  Snp = EFI_SIMPLE_NETWORK_DEV_FROM_THIS (This);

  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);

  //
  // Return error if the SNP is not initialized.
  //
  switch (Snp->Mode.State) {
    case EfiSimpleNetworkInitialized:
      break;

    case EfiSimpleNetworkStopped:
      Status = EFI_NOT_STARTED;
      goto ON_EXIT;

    default:
      Status = EFI_DEVICE_ERROR;
      goto ON_EXIT;
  }

  if (Reset) {
    Status = PxeSetStnAddr (Snp, NULL);
  } else {
    Status = PxeSetStnAddr (Snp, New);
  }

ON_EXIT:
  gBS->RestoreTPL (OldTpl);

  return Status;
}