mirror of https://github.com/acidanthera/audk.git
1801 lines
44 KiB
C
1801 lines
44 KiB
C
/** @file
|
|
Network library functions providing net buffer operation support.
|
|
|
|
Copyright (c) 2005 - 2009, Intel Corporation.<BR>
|
|
All rights reserved. This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
**/
|
|
|
|
#include <Uefi.h>
|
|
|
|
#include <Library/NetLib.h>
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
|
|
|
|
/**
|
|
Allocate and build up the sketch for a NET_BUF.
|
|
|
|
The net buffer allocated has the BlockOpNum's NET_BLOCK_OP, and its associated
|
|
NET_VECTOR has the BlockNum's NET_BLOCK. But all the NET_BLOCK_OP and
|
|
NET_BLOCK remain un-initialized.
|
|
|
|
@param[in] BlockNum The number of NET_BLOCK in the vector of net buffer
|
|
@param[in] BlockOpNum The number of NET_BLOCK_OP in the net buffer
|
|
|
|
@return Pointer to the allocated NET_BUF, or NULL if the
|
|
allocation failed due to resource limit.
|
|
|
|
**/
|
|
NET_BUF *
|
|
NetbufAllocStruct (
|
|
IN UINT32 BlockNum,
|
|
IN UINT32 BlockOpNum
|
|
)
|
|
{
|
|
NET_BUF *Nbuf;
|
|
NET_VECTOR *Vector;
|
|
|
|
ASSERT (BlockOpNum >= 1);
|
|
|
|
//
|
|
// Allocate three memory blocks.
|
|
//
|
|
Nbuf = AllocateZeroPool (NET_BUF_SIZE (BlockOpNum));
|
|
|
|
if (Nbuf == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
Nbuf->Signature = NET_BUF_SIGNATURE;
|
|
Nbuf->RefCnt = 1;
|
|
Nbuf->BlockOpNum = BlockOpNum;
|
|
InitializeListHead (&Nbuf->List);
|
|
|
|
if (BlockNum != 0) {
|
|
Vector = AllocateZeroPool (NET_VECTOR_SIZE (BlockNum));
|
|
|
|
if (Vector == NULL) {
|
|
goto FreeNbuf;
|
|
}
|
|
|
|
Vector->Signature = NET_VECTOR_SIGNATURE;
|
|
Vector->RefCnt = 1;
|
|
Vector->BlockNum = BlockNum;
|
|
Nbuf->Vector = Vector;
|
|
}
|
|
|
|
return Nbuf;
|
|
|
|
FreeNbuf:
|
|
|
|
gBS->FreePool (Nbuf);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
Allocate a single block NET_BUF. Upon allocation, all the
|
|
free space is in the tail room.
|
|
|
|
@param[in] Len The length of the block.
|
|
|
|
@return Pointer to the allocated NET_BUF, or NULL if the
|
|
allocation failed due to resource limit.
|
|
|
|
**/
|
|
NET_BUF *
|
|
EFIAPI
|
|
NetbufAlloc (
|
|
IN UINT32 Len
|
|
)
|
|
{
|
|
NET_BUF *Nbuf;
|
|
NET_VECTOR *Vector;
|
|
UINT8 *Bulk;
|
|
|
|
ASSERT (Len > 0);
|
|
|
|
Nbuf = NetbufAllocStruct (1, 1);
|
|
|
|
if (Nbuf == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
Bulk = AllocatePool (Len);
|
|
|
|
if (Bulk == NULL) {
|
|
goto FreeNBuf;
|
|
}
|
|
|
|
Vector = Nbuf->Vector;
|
|
Vector->Len = Len;
|
|
|
|
Vector->Block[0].Bulk = Bulk;
|
|
Vector->Block[0].Len = Len;
|
|
|
|
Nbuf->BlockOp[0].BlockHead = Bulk;
|
|
Nbuf->BlockOp[0].BlockTail = Bulk + Len;
|
|
|
|
Nbuf->BlockOp[0].Head = Bulk;
|
|
Nbuf->BlockOp[0].Tail = Bulk;
|
|
Nbuf->BlockOp[0].Size = 0;
|
|
|
|
return Nbuf;
|
|
|
|
FreeNBuf:
|
|
gBS->FreePool (Nbuf);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
Free the net vector.
|
|
|
|
Decrease the reference count of the net vector by one. The real resource free
|
|
operation isn't performed until the reference count of the net vector is
|
|
decreased to 0.
|
|
|
|
@param[in] Vector Pointer to the NET_VECTOR to be freed.
|
|
|
|
**/
|
|
VOID
|
|
NetbufFreeVector (
|
|
IN NET_VECTOR *Vector
|
|
)
|
|
{
|
|
UINT32 Index;
|
|
|
|
NET_CHECK_SIGNATURE (Vector, NET_VECTOR_SIGNATURE);
|
|
ASSERT (Vector->RefCnt > 0);
|
|
|
|
Vector->RefCnt--;
|
|
|
|
if (Vector->RefCnt > 0) {
|
|
return;
|
|
}
|
|
|
|
if (Vector->Free != NULL) {
|
|
//
|
|
// Call external free function to free the vector if it
|
|
// isn't NULL. If NET_VECTOR_OWN_FIRST is set, release the
|
|
// first block since it is allocated by us
|
|
//
|
|
if ((Vector->Flag & NET_VECTOR_OWN_FIRST) != 0) {
|
|
gBS->FreePool (Vector->Block[0].Bulk);
|
|
}
|
|
|
|
Vector->Free (Vector->Arg);
|
|
|
|
} else {
|
|
//
|
|
// Free each memory block associated with the Vector
|
|
//
|
|
for (Index = 0; Index < Vector->BlockNum; Index++) {
|
|
gBS->FreePool (Vector->Block[Index].Bulk);
|
|
}
|
|
}
|
|
|
|
gBS->FreePool (Vector);
|
|
}
|
|
|
|
|
|
/**
|
|
Free the net buffer and its associated NET_VECTOR.
|
|
|
|
Decrease the reference count of the net buffer by one. Free the associated net
|
|
vector and itself if the reference count of the net buffer is decreased to 0.
|
|
The net vector free operation just decrease the reference count of the net
|
|
vector by one and do the real resource free operation when the reference count
|
|
of the net vector is 0.
|
|
|
|
@param[in] Nbuf Pointer to the NET_BUF to be freed.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
NetbufFree (
|
|
IN NET_BUF *Nbuf
|
|
)
|
|
{
|
|
NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
|
|
ASSERT (Nbuf->RefCnt > 0);
|
|
|
|
Nbuf->RefCnt--;
|
|
|
|
if (Nbuf->RefCnt == 0) {
|
|
//
|
|
// Update Vector only when NBuf is to be released. That is,
|
|
// all the sharing of Nbuf increse Vector's RefCnt by one
|
|
//
|
|
NetbufFreeVector (Nbuf->Vector);
|
|
gBS->FreePool (Nbuf);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Create a copy of the net buffer that shares the associated net vector.
|
|
|
|
The reference count of the newly created net buffer is set to 1. The reference
|
|
count of the associated net vector is increased by one.
|
|
|
|
@param[in] Nbuf Pointer to the net buffer to be cloned.
|
|
|
|
@return Pointer to the cloned net buffer, or NULL if the
|
|
allocation failed due to resource limit.
|
|
|
|
**/
|
|
NET_BUF *
|
|
EFIAPI
|
|
NetbufClone (
|
|
IN NET_BUF *Nbuf
|
|
)
|
|
{
|
|
NET_BUF *Clone;
|
|
|
|
NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
|
|
|
|
Clone = AllocatePool (NET_BUF_SIZE (Nbuf->BlockOpNum));
|
|
|
|
if (Clone == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
Clone->Signature = NET_BUF_SIGNATURE;
|
|
Clone->RefCnt = 1;
|
|
InitializeListHead (&Clone->List);
|
|
|
|
Clone->Ip = Nbuf->Ip;
|
|
Clone->Tcp = Nbuf->Tcp;
|
|
|
|
CopyMem (Clone->ProtoData, Nbuf->ProtoData, NET_PROTO_DATA);
|
|
|
|
NET_GET_REF (Nbuf->Vector);
|
|
|
|
Clone->Vector = Nbuf->Vector;
|
|
Clone->BlockOpNum = Nbuf->BlockOpNum;
|
|
Clone->TotalSize = Nbuf->TotalSize;
|
|
CopyMem (Clone->BlockOp, Nbuf->BlockOp, sizeof (NET_BLOCK_OP) * Nbuf->BlockOpNum);
|
|
|
|
return Clone;
|
|
}
|
|
|
|
|
|
/**
|
|
Create a duplicated copy of the net buffer with data copied and HeadSpace
|
|
bytes of head space reserved.
|
|
|
|
The duplicated net buffer will allocate its own memory to hold the data of the
|
|
source net buffer.
|
|
|
|
@param[in] Nbuf Pointer to the net buffer to be duplicated from.
|
|
@param[in, out] Duplicate Pointer to the net buffer to duplicate to, if
|
|
NULL a new net buffer is allocated.
|
|
@param[in] HeadSpace Length of the head space to reserve.
|
|
|
|
@return Pointer to the duplicated net buffer, or NULL if
|
|
the allocation failed due to resource limit.
|
|
|
|
**/
|
|
NET_BUF *
|
|
EFIAPI
|
|
NetbufDuplicate (
|
|
IN NET_BUF *Nbuf,
|
|
IN OUT NET_BUF *Duplicate OPTIONAL,
|
|
IN UINT32 HeadSpace
|
|
)
|
|
{
|
|
UINT8 *Dst;
|
|
|
|
NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
|
|
|
|
if (Duplicate == NULL) {
|
|
Duplicate = NetbufAlloc (Nbuf->TotalSize + HeadSpace);
|
|
}
|
|
|
|
if (Duplicate == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Don't set the IP and TCP head point, since it is most
|
|
// like that they are pointing to the memory of Nbuf.
|
|
//
|
|
CopyMem (Duplicate->ProtoData, Nbuf->ProtoData, NET_PROTO_DATA);
|
|
NetbufReserve (Duplicate, HeadSpace);
|
|
|
|
Dst = NetbufAllocSpace (Duplicate, Nbuf->TotalSize, NET_BUF_TAIL);
|
|
NetbufCopy (Nbuf, 0, Nbuf->TotalSize, Dst);
|
|
|
|
return Duplicate;
|
|
}
|
|
|
|
|
|
/**
|
|
Free a list of net buffers.
|
|
|
|
@param[in, out] Head Pointer to the head of linked net buffers.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
NetbufFreeList (
|
|
IN OUT LIST_ENTRY *Head
|
|
)
|
|
{
|
|
LIST_ENTRY *Entry;
|
|
LIST_ENTRY *Next;
|
|
NET_BUF *Nbuf;
|
|
|
|
Entry = Head->ForwardLink;
|
|
|
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) {
|
|
Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
|
|
NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
|
|
|
|
RemoveEntryList (Entry);
|
|
NetbufFree (Nbuf);
|
|
}
|
|
|
|
ASSERT (IsListEmpty (Head));
|
|
}
|
|
|
|
|
|
/**
|
|
Get the index of NET_BLOCK_OP that contains the byte at Offset in the net
|
|
buffer.
|
|
|
|
This can be used to, for example, retrieve the IP header in the packet. It
|
|
also can be used to get the fragment that contains the byte which is used
|
|
mainly by the library implementation itself.
|
|
|
|
@param[in] Nbuf Pointer to the net buffer.
|
|
@param[in] Offset The offset of the byte.
|
|
@param[out] Index Index of the NET_BLOCK_OP that contains the byte at
|
|
Offset.
|
|
|
|
@return Pointer to the Offset'th byte of data in the net buffer, or NULL
|
|
if there is no such data in the net buffer.
|
|
|
|
**/
|
|
UINT8 *
|
|
EFIAPI
|
|
NetbufGetByte (
|
|
IN NET_BUF *Nbuf,
|
|
IN UINT32 Offset,
|
|
OUT UINT32 *Index OPTIONAL
|
|
)
|
|
{
|
|
NET_BLOCK_OP *BlockOp;
|
|
UINT32 Loop;
|
|
UINT32 Len;
|
|
|
|
NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
|
|
|
|
if (Offset >= Nbuf->TotalSize) {
|
|
return NULL;
|
|
}
|
|
|
|
BlockOp = Nbuf->BlockOp;
|
|
Len = 0;
|
|
|
|
for (Loop = 0; Loop < Nbuf->BlockOpNum; Loop++) {
|
|
|
|
if (Len + BlockOp[Loop].Size <= Offset) {
|
|
Len += BlockOp[Loop].Size;
|
|
continue;
|
|
}
|
|
|
|
if (Index != NULL) {
|
|
*Index = Loop;
|
|
}
|
|
|
|
return BlockOp[Loop].Head + (Offset - Len);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Set the NET_BLOCK and corresponding NET_BLOCK_OP in the net buffer and
|
|
corresponding net vector according to the bulk pointer and bulk length.
|
|
|
|
All the pointers in the Index'th NET_BLOCK and NET_BLOCK_OP are set to the
|
|
bulk's head and tail respectively. So, this function alone can't be used by
|
|
NetbufAlloc.
|
|
|
|
@param[in, out] Nbuf Pointer to the net buffer.
|
|
@param[in] Bulk Pointer to the data.
|
|
@param[in] Len Length of the bulk data.
|
|
@param[in] Index The data block index in the net buffer the bulk
|
|
data should belong to.
|
|
|
|
**/
|
|
VOID
|
|
NetbufSetBlock (
|
|
IN OUT NET_BUF *Nbuf,
|
|
IN UINT8 *Bulk,
|
|
IN UINT32 Len,
|
|
IN UINT32 Index
|
|
)
|
|
{
|
|
NET_BLOCK_OP *BlockOp;
|
|
NET_BLOCK *Block;
|
|
|
|
NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
|
|
NET_CHECK_SIGNATURE (Nbuf->Vector, NET_VECTOR_SIGNATURE);
|
|
ASSERT (Index < Nbuf->BlockOpNum);
|
|
|
|
Block = &(Nbuf->Vector->Block[Index]);
|
|
BlockOp = &(Nbuf->BlockOp[Index]);
|
|
Block->Len = Len;
|
|
Block->Bulk = Bulk;
|
|
BlockOp->BlockHead = Bulk;
|
|
BlockOp->BlockTail = Bulk + Len;
|
|
BlockOp->Head = Bulk;
|
|
BlockOp->Tail = Bulk + Len;
|
|
BlockOp->Size = Len;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Set the NET_BLOCK_OP in the net buffer. The corresponding NET_BLOCK
|
|
structure is left untouched.
|
|
|
|
Some times, there is no 1:1 relationship between NET_BLOCK and NET_BLOCK_OP.
|
|
For example, that in NetbufGetFragment.
|
|
|
|
@param[in, out] Nbuf Pointer to the net buffer.
|
|
@param[in] Bulk Pointer to the data.
|
|
@param[in] Len Length of the bulk data.
|
|
@param[in] Index The data block index in the net buffer the bulk
|
|
data should belong to.
|
|
|
|
**/
|
|
VOID
|
|
NetbufSetBlockOp (
|
|
IN OUT NET_BUF *Nbuf,
|
|
IN UINT8 *Bulk,
|
|
IN UINT32 Len,
|
|
IN UINT32 Index
|
|
)
|
|
{
|
|
NET_BLOCK_OP *BlockOp;
|
|
|
|
NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
|
|
ASSERT (Index < Nbuf->BlockOpNum);
|
|
|
|
BlockOp = &(Nbuf->BlockOp[Index]);
|
|
BlockOp->BlockHead = Bulk;
|
|
BlockOp->BlockTail = Bulk + Len;
|
|
BlockOp->Head = Bulk;
|
|
BlockOp->Tail = Bulk + Len;
|
|
BlockOp->Size = Len;
|
|
}
|
|
|
|
|
|
/**
|
|
Helper function for NetbufGetFragment. NetbufGetFragment may allocate the
|
|
first block to reserve HeadSpace bytes header space. So it needs to create a
|
|
new net vector for the first block and can avoid copy for the remaining data
|
|
by sharing the old net vector.
|
|
|
|
@param[in] Arg Point to the old NET_VECTOR.
|
|
|
|
**/
|
|
VOID
|
|
NetbufGetFragmentFree (
|
|
IN VOID *Arg
|
|
)
|
|
{
|
|
NET_VECTOR *Vector;
|
|
|
|
Vector = (NET_VECTOR *)Arg;
|
|
NetbufFreeVector (Vector);
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Create a NET_BUF structure which contains Len byte data of Nbuf starting from
|
|
Offset.
|
|
|
|
A new NET_BUF structure will be created but the associated data in NET_VECTOR
|
|
is shared. This function exists to do IP packet fragmentation.
|
|
|
|
@param[in] Nbuf Pointer to the net buffer to be extracted.
|
|
@param[in] Offset Starting point of the data to be included in the new
|
|
net buffer.
|
|
@param[in] Len Bytes of data to be included in the new net buffer.
|
|
@param[in] HeadSpace Bytes of head space to reserve for protocol header.
|
|
|
|
@return Pointer to the cloned net buffer, or NULL if the
|
|
allocation failed due to resource limit.
|
|
|
|
**/
|
|
NET_BUF *
|
|
EFIAPI
|
|
NetbufGetFragment (
|
|
IN NET_BUF *Nbuf,
|
|
IN UINT32 Offset,
|
|
IN UINT32 Len,
|
|
IN UINT32 HeadSpace
|
|
)
|
|
{
|
|
NET_BUF *Child;
|
|
NET_VECTOR *Vector;
|
|
NET_BLOCK_OP *BlockOp;
|
|
UINT32 CurBlockOp;
|
|
UINT32 BlockOpNum;
|
|
UINT8 *FirstBulk;
|
|
UINT32 Index;
|
|
UINT32 First;
|
|
UINT32 Last;
|
|
UINT32 FirstSkip;
|
|
UINT32 FirstLen;
|
|
UINT32 LastLen;
|
|
UINT32 Cur;
|
|
|
|
NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
|
|
|
|
if ((Len == 0) || (Offset + Len > Nbuf->TotalSize)) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// First find the first and last BlockOp that contains
|
|
// the valid data, and compute the offset of the first
|
|
// BlockOp and length of the last BlockOp
|
|
//
|
|
BlockOp = Nbuf->BlockOp;
|
|
Cur = 0;
|
|
|
|
for (Index = 0; Index < Nbuf->BlockOpNum; Index++) {
|
|
if (Offset < Cur + BlockOp[Index].Size) {
|
|
break;
|
|
}
|
|
|
|
Cur += BlockOp[Index].Size;
|
|
}
|
|
|
|
//
|
|
// First is the index of the first BlockOp, FirstSkip is
|
|
// the offset of the first byte in the first BlockOp.
|
|
//
|
|
First = Index;
|
|
FirstSkip = Offset - Cur;
|
|
FirstLen = BlockOp[Index].Size - FirstSkip;
|
|
|
|
//
|
|
//redundant assignment to make compiler happy.
|
|
//
|
|
Last = 0;
|
|
LastLen = 0;
|
|
|
|
if (Len > FirstLen) {
|
|
Cur += BlockOp[Index].Size;
|
|
Index++;
|
|
|
|
for (; Index < Nbuf->BlockOpNum; Index++) {
|
|
if (Offset + Len <= Cur + BlockOp[Index].Size) {
|
|
Last = Index;
|
|
LastLen = Offset + Len - Cur;
|
|
break;
|
|
}
|
|
|
|
Cur += BlockOp[Index].Size;
|
|
}
|
|
|
|
} else {
|
|
Last = First;
|
|
LastLen = Len;
|
|
FirstLen = Len;
|
|
}
|
|
|
|
BlockOpNum = Last - First + 1;
|
|
CurBlockOp = 0;
|
|
|
|
if (HeadSpace != 0) {
|
|
//
|
|
// Allocate an extra block to accomdate the head space.
|
|
//
|
|
BlockOpNum++;
|
|
|
|
Child = NetbufAllocStruct (1, BlockOpNum);
|
|
|
|
if (Child == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
FirstBulk = AllocatePool (HeadSpace);
|
|
|
|
if (FirstBulk == NULL) {
|
|
goto FreeChild;
|
|
}
|
|
|
|
Vector = Child->Vector;
|
|
Vector->Free = NetbufGetFragmentFree;
|
|
Vector->Arg = Nbuf->Vector;
|
|
Vector->Flag = NET_VECTOR_OWN_FIRST;
|
|
Vector->Len = HeadSpace;
|
|
|
|
//
|
|
// Reserve the head space in the first block
|
|
//
|
|
NetbufSetBlock (Child, FirstBulk, HeadSpace, 0);
|
|
Child->BlockOp[0].Head += HeadSpace;
|
|
Child->BlockOp[0].Size = 0;
|
|
CurBlockOp++;
|
|
|
|
}else {
|
|
Child = NetbufAllocStruct (0, BlockOpNum);
|
|
|
|
if (Child == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
Child->Vector = Nbuf->Vector;
|
|
}
|
|
|
|
NET_GET_REF (Nbuf->Vector);
|
|
Child->TotalSize = Len;
|
|
|
|
//
|
|
// Set all the BlockOp up, the first and last one are special
|
|
// and need special process.
|
|
//
|
|
NetbufSetBlockOp (
|
|
Child,
|
|
Nbuf->BlockOp[First].Head + FirstSkip,
|
|
FirstLen,
|
|
CurBlockOp++
|
|
);
|
|
|
|
for (Index = First + 1; Index <= Last - 1 ; Index++) {
|
|
NetbufSetBlockOp (
|
|
Child,
|
|
BlockOp[Index].Head,
|
|
BlockOp[Index].Size,
|
|
CurBlockOp++
|
|
);
|
|
}
|
|
|
|
if (First != Last) {
|
|
NetbufSetBlockOp (
|
|
Child,
|
|
BlockOp[Last].Head,
|
|
LastLen,
|
|
CurBlockOp
|
|
);
|
|
}
|
|
|
|
CopyMem (Child->ProtoData, Nbuf->ProtoData, NET_PROTO_DATA);
|
|
return Child;
|
|
|
|
FreeChild:
|
|
|
|
gBS->FreePool (Child);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Build a NET_BUF from external blocks.
|
|
|
|
A new NET_BUF structure will be created from external blocks. Additional block
|
|
of memory will be allocated to hold reserved HeadSpace bytes of header room
|
|
and existing HeadLen bytes of header but the external blocks are shared by the
|
|
net buffer to avoid data copying.
|
|
|
|
@param[in] ExtFragment Pointer to the data block.
|
|
@param[in] ExtNum The number of the data blocks.
|
|
@param[in] HeadSpace The head space to be reserved.
|
|
@param[in] HeadLen The length of the protocol header, This function
|
|
will pull that number of data into a linear block.
|
|
@param[in] ExtFree Pointer to the caller provided free function.
|
|
@param[in] Arg The argument passed to ExtFree when ExtFree is
|
|
called.
|
|
|
|
@return Pointer to the net buffer built from the data blocks,
|
|
or NULL if the allocation failed due to resource
|
|
limit.
|
|
|
|
**/
|
|
NET_BUF *
|
|
EFIAPI
|
|
NetbufFromExt (
|
|
IN NET_FRAGMENT *ExtFragment,
|
|
IN UINT32 ExtNum,
|
|
IN UINT32 HeadSpace,
|
|
IN UINT32 HeadLen,
|
|
IN NET_VECTOR_EXT_FREE ExtFree,
|
|
IN VOID *Arg OPTIONAL
|
|
)
|
|
{
|
|
NET_BUF *Nbuf;
|
|
NET_VECTOR *Vector;
|
|
NET_FRAGMENT SavedFragment;
|
|
UINT32 SavedIndex;
|
|
UINT32 TotalLen;
|
|
UINT32 BlockNum;
|
|
UINT8 *FirstBlock;
|
|
UINT32 FirstBlockLen;
|
|
UINT8 *Header;
|
|
UINT32 CurBlock;
|
|
UINT32 Index;
|
|
UINT32 Len;
|
|
UINT32 Copied;
|
|
|
|
ASSERT ((ExtFragment != NULL) && (ExtNum > 0) && (ExtFree != NULL));
|
|
|
|
SavedFragment.Bulk = NULL;
|
|
SavedFragment.Len = 0;
|
|
|
|
FirstBlockLen = 0;
|
|
FirstBlock = NULL;
|
|
BlockNum = ExtNum;
|
|
Index = 0;
|
|
TotalLen = 0;
|
|
SavedIndex = 0;
|
|
Len = 0;
|
|
Copied = 0;
|
|
|
|
//
|
|
// No need to consolidate the header if the first block is
|
|
// longer than the header length or there is only one block.
|
|
//
|
|
if ((ExtFragment[0].Len >= HeadLen) || (ExtNum == 1)) {
|
|
HeadLen = 0;
|
|
}
|
|
|
|
//
|
|
// Allocate an extra block if we need to:
|
|
// 1. Allocate some header space
|
|
// 2. aggreate the packet header
|
|
//
|
|
if ((HeadSpace != 0) || (HeadLen != 0)) {
|
|
FirstBlockLen = HeadLen + HeadSpace;
|
|
FirstBlock = AllocatePool (FirstBlockLen);
|
|
|
|
if (FirstBlock == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
BlockNum++;
|
|
}
|
|
|
|
//
|
|
// Copy the header to the first block, reduce the NET_BLOCK
|
|
// to allocate by one for each block that is completely covered
|
|
// by the first bulk.
|
|
//
|
|
if (HeadLen != 0) {
|
|
Len = HeadLen;
|
|
Header = FirstBlock + HeadSpace;
|
|
|
|
for (Index = 0; Index < ExtNum; Index++) {
|
|
if (Len >= ExtFragment[Index].Len) {
|
|
CopyMem (Header, ExtFragment[Index].Bulk, ExtFragment[Index].Len);
|
|
|
|
Copied += ExtFragment[Index].Len;
|
|
Len -= ExtFragment[Index].Len;
|
|
Header += ExtFragment[Index].Len;
|
|
TotalLen += ExtFragment[Index].Len;
|
|
BlockNum--;
|
|
|
|
if (Len == 0) {
|
|
//
|
|
// Increament the index number to point to the next
|
|
// non-empty fragment.
|
|
//
|
|
Index++;
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
CopyMem (Header, ExtFragment[Index].Bulk, Len);
|
|
|
|
Copied += Len;
|
|
TotalLen += Len;
|
|
|
|
//
|
|
// Adjust the block structure to exclude the data copied,
|
|
// So, the left-over block can be processed as other blocks.
|
|
// But it must be recovered later. (SavedIndex > 0) always
|
|
// holds since we don't aggreate the header if the first block
|
|
// is bigger enough that the header is continuous
|
|
//
|
|
SavedIndex = Index;
|
|
SavedFragment = ExtFragment[Index];
|
|
ExtFragment[Index].Bulk += Len;
|
|
ExtFragment[Index].Len -= Len;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Nbuf = NetbufAllocStruct (BlockNum, BlockNum);
|
|
|
|
if (Nbuf == NULL) {
|
|
goto FreeFirstBlock;
|
|
}
|
|
|
|
Vector = Nbuf->Vector;
|
|
Vector->Free = ExtFree;
|
|
Vector->Arg = Arg;
|
|
Vector->Flag = ((FirstBlockLen != 0) ? NET_VECTOR_OWN_FIRST : 0);
|
|
|
|
//
|
|
// Set the first block up which may contain
|
|
// some head space and aggregated header
|
|
//
|
|
CurBlock = 0;
|
|
|
|
if (FirstBlockLen != 0) {
|
|
NetbufSetBlock (Nbuf, FirstBlock, HeadSpace + Copied, 0);
|
|
Nbuf->BlockOp[0].Head += HeadSpace;
|
|
Nbuf->BlockOp[0].Size = Copied;
|
|
|
|
CurBlock++;
|
|
}
|
|
|
|
for (; Index < ExtNum; Index++) {
|
|
NetbufSetBlock (Nbuf, ExtFragment[Index].Bulk, ExtFragment[Index].Len, CurBlock);
|
|
TotalLen += ExtFragment[Index].Len;
|
|
CurBlock++;
|
|
}
|
|
|
|
Vector->Len = TotalLen + HeadSpace;
|
|
Nbuf->TotalSize = TotalLen;
|
|
|
|
if (SavedIndex != 0) {
|
|
ExtFragment[SavedIndex] = SavedFragment;
|
|
}
|
|
|
|
return Nbuf;
|
|
|
|
FreeFirstBlock:
|
|
gBS->FreePool (FirstBlock);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
Build a fragment table to contain the fragments in the net buffer. This is the
|
|
opposite operation of the NetbufFromExt.
|
|
|
|
@param[in] Nbuf Point to the net buffer.
|
|
@param[in, out] ExtFragment Pointer to the data block.
|
|
@param[in, out] ExtNum The number of the data blocks.
|
|
|
|
@retval EFI_BUFFER_TOO_SMALL The number of non-empty block is bigger than
|
|
ExtNum.
|
|
@retval EFI_SUCCESS Fragment table is built successfully.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
NetbufBuildExt (
|
|
IN NET_BUF *Nbuf,
|
|
IN OUT NET_FRAGMENT *ExtFragment,
|
|
IN OUT UINT32 *ExtNum
|
|
)
|
|
{
|
|
UINT32 Index;
|
|
UINT32 Current;
|
|
|
|
Current = 0;
|
|
|
|
for (Index = 0; (Index < Nbuf->BlockOpNum); Index++) {
|
|
if (Nbuf->BlockOp[Index].Size == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (Current < *ExtNum) {
|
|
ExtFragment[Current].Bulk = Nbuf->BlockOp[Index].Head;
|
|
ExtFragment[Current].Len = Nbuf->BlockOp[Index].Size;
|
|
Current++;
|
|
} else {
|
|
return EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
}
|
|
|
|
*ExtNum = Current;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
|
|
/**
|
|
Build a net buffer from a list of net buffers.
|
|
|
|
All the fragments will be collected from the list of NEW_BUF and then a new
|
|
net buffer will be created through NetbufFromExt.
|
|
|
|
@param[in] BufList A List of the net buffer.
|
|
@param[in] HeadSpace The head space to be reserved.
|
|
@param[in] HeaderLen The length of the protocol header, This function
|
|
will pull that number of data into a linear block.
|
|
@param[in] ExtFree Pointer to the caller provided free function.
|
|
@param[in] Arg The argument passed to ExtFree when ExtFree is called.
|
|
|
|
@return Pointer to the net buffer built from the list of net
|
|
buffers.
|
|
|
|
**/
|
|
NET_BUF *
|
|
EFIAPI
|
|
NetbufFromBufList (
|
|
IN LIST_ENTRY *BufList,
|
|
IN UINT32 HeadSpace,
|
|
IN UINT32 HeaderLen,
|
|
IN NET_VECTOR_EXT_FREE ExtFree,
|
|
IN VOID *Arg OPTIONAL
|
|
)
|
|
{
|
|
NET_FRAGMENT *Fragment;
|
|
UINT32 FragmentNum;
|
|
LIST_ENTRY *Entry;
|
|
NET_BUF *Nbuf;
|
|
UINT32 Index;
|
|
UINT32 Current;
|
|
|
|
//
|
|
//Compute how many blocks are there
|
|
//
|
|
FragmentNum = 0;
|
|
|
|
NET_LIST_FOR_EACH (Entry, BufList) {
|
|
Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
|
|
NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
|
|
FragmentNum += Nbuf->BlockOpNum;
|
|
}
|
|
|
|
//
|
|
//Allocate and copy block points
|
|
//
|
|
Fragment = AllocatePool (sizeof (NET_FRAGMENT) * FragmentNum);
|
|
|
|
if (Fragment == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
Current = 0;
|
|
|
|
NET_LIST_FOR_EACH (Entry, BufList) {
|
|
Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
|
|
NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
|
|
|
|
for (Index = 0; Index < Nbuf->BlockOpNum; Index++) {
|
|
if (Nbuf->BlockOp[Index].Size != 0) {
|
|
Fragment[Current].Bulk = Nbuf->BlockOp[Index].Head;
|
|
Fragment[Current].Len = Nbuf->BlockOp[Index].Size;
|
|
Current++;
|
|
}
|
|
}
|
|
}
|
|
|
|
Nbuf = NetbufFromExt (Fragment, Current, HeadSpace, HeaderLen, ExtFree, Arg);
|
|
gBS->FreePool (Fragment);
|
|
|
|
return Nbuf;
|
|
}
|
|
|
|
|
|
/**
|
|
Reserve some space in the header room of the net buffer.
|
|
|
|
Upon allocation, all the space are in the tail room of the buffer. Call this
|
|
function to move some space to the header room. This function is quite limited
|
|
in that it can only reserve space from the first block of an empty NET_BUF not
|
|
built from the external. But it should be enough for the network stack.
|
|
|
|
@param[in, out] Nbuf Pointer to the net buffer.
|
|
@param[in] Len The length of buffer to be reserved from the header.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
NetbufReserve (
|
|
IN OUT NET_BUF *Nbuf,
|
|
IN UINT32 Len
|
|
)
|
|
{
|
|
NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
|
|
NET_CHECK_SIGNATURE (Nbuf->Vector, NET_VECTOR_SIGNATURE);
|
|
|
|
ASSERT ((Nbuf->BlockOpNum == 1) && (Nbuf->TotalSize == 0));
|
|
ASSERT ((Nbuf->Vector->Free == NULL) && (Nbuf->Vector->Len >= Len));
|
|
|
|
Nbuf->BlockOp[0].Head += Len;
|
|
Nbuf->BlockOp[0].Tail += Len;
|
|
|
|
ASSERT (Nbuf->BlockOp[0].Tail <= Nbuf->BlockOp[0].BlockTail);
|
|
}
|
|
|
|
|
|
/**
|
|
Allocate Len bytes of space from the header or tail of the buffer.
|
|
|
|
@param[in, out] Nbuf Pointer to the net buffer.
|
|
@param[in] Len The length of the buffer to be allocated.
|
|
@param[in] FromHead The flag to indicate whether reserve the data
|
|
from head (TRUE) or tail (FALSE).
|
|
|
|
@return Pointer to the first byte of the allocated buffer,
|
|
or NULL if there is no sufficient space.
|
|
|
|
**/
|
|
UINT8*
|
|
EFIAPI
|
|
NetbufAllocSpace (
|
|
IN OUT NET_BUF *Nbuf,
|
|
IN UINT32 Len,
|
|
IN BOOLEAN FromHead
|
|
)
|
|
{
|
|
NET_BLOCK_OP *BlockOp;
|
|
UINT32 Index;
|
|
UINT8 *SavedTail;
|
|
|
|
NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
|
|
NET_CHECK_SIGNATURE (Nbuf->Vector, NET_VECTOR_SIGNATURE);
|
|
|
|
ASSERT (Len > 0);
|
|
|
|
if (FromHead) {
|
|
//
|
|
// Allocate some space from head. If the buffer is empty,
|
|
// allocate from the first block. If it isn't, allocate
|
|
// from the first non-empty block, or the block before that.
|
|
//
|
|
if (Nbuf->TotalSize == 0) {
|
|
Index = 0;
|
|
} else {
|
|
NetbufGetByte (Nbuf, 0, &Index);
|
|
|
|
if ((NET_HEADSPACE(&(Nbuf->BlockOp[Index])) < Len) && (Index > 0)) {
|
|
Index--;
|
|
}
|
|
}
|
|
|
|
BlockOp = &(Nbuf->BlockOp[Index]);
|
|
|
|
if (NET_HEADSPACE (BlockOp) < Len) {
|
|
return NULL;
|
|
}
|
|
|
|
BlockOp->Head -= Len;
|
|
BlockOp->Size += Len;
|
|
Nbuf->TotalSize += Len;
|
|
|
|
return BlockOp->Head;
|
|
|
|
} else {
|
|
//
|
|
// Allocate some space from the tail. If the buffer is empty,
|
|
// allocate from the first block. If it isn't, allocate
|
|
// from the last non-empty block, or the block after that.
|
|
//
|
|
if (Nbuf->TotalSize == 0) {
|
|
Index = 0;
|
|
} else {
|
|
NetbufGetByte (Nbuf, Nbuf->TotalSize - 1, &Index);
|
|
|
|
if ((NET_TAILSPACE(&(Nbuf->BlockOp[Index])) < Len) &&
|
|
(Index < Nbuf->BlockOpNum - 1)) {
|
|
|
|
Index++;
|
|
}
|
|
}
|
|
|
|
BlockOp = &(Nbuf->BlockOp[Index]);
|
|
|
|
if (NET_TAILSPACE (BlockOp) < Len) {
|
|
return NULL;
|
|
}
|
|
|
|
SavedTail = BlockOp->Tail;
|
|
|
|
BlockOp->Tail += Len;
|
|
BlockOp->Size += Len;
|
|
Nbuf->TotalSize += Len;
|
|
|
|
return SavedTail;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Trim a single NET_BLOCK by Len bytes from the header or tail.
|
|
|
|
@param[in, out] BlockOp Pointer to the NET_BLOCK.
|
|
@param[in] Len The length of the data to be trimmed.
|
|
@param[in] FromHead The flag to indicate whether trim data from head
|
|
(TRUE) or tail (FALSE).
|
|
|
|
**/
|
|
VOID
|
|
NetblockTrim (
|
|
IN OUT NET_BLOCK_OP *BlockOp,
|
|
IN UINT32 Len,
|
|
IN BOOLEAN FromHead
|
|
)
|
|
{
|
|
ASSERT ((BlockOp != NULL) && (BlockOp->Size >= Len));
|
|
|
|
BlockOp->Size -= Len;
|
|
|
|
if (FromHead) {
|
|
BlockOp->Head += Len;
|
|
} else {
|
|
BlockOp->Tail -= Len;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Trim Len bytes from the header or tail of the net buffer.
|
|
|
|
@param[in, out] Nbuf Pointer to the net buffer.
|
|
@param[in] Len The length of the data to be trimmed.
|
|
@param[in] FromHead The flag to indicate whether trim data from head
|
|
(TRUE) or tail (FALSE).
|
|
|
|
@return Length of the actually trimmed data, which is possible to be less
|
|
than Len because the TotalSize of Nbuf is less than Len.
|
|
|
|
**/
|
|
UINT32
|
|
EFIAPI
|
|
NetbufTrim (
|
|
IN OUT NET_BUF *Nbuf,
|
|
IN UINT32 Len,
|
|
IN BOOLEAN FromHead
|
|
)
|
|
{
|
|
NET_BLOCK_OP *BlockOp;
|
|
UINT32 Index;
|
|
UINT32 Trimmed;
|
|
|
|
NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
|
|
|
|
if (Len > Nbuf->TotalSize) {
|
|
Len = Nbuf->TotalSize;
|
|
}
|
|
|
|
//
|
|
// If FromTail is true, iterate backward. That
|
|
// is, init Index to NBuf->BlockNum - 1, and
|
|
// decrease it by 1 during each loop. Otherwise,
|
|
// iterate forward. That is, init Index to 0, and
|
|
// increase it by 1 during each loop.
|
|
//
|
|
Trimmed = 0;
|
|
Nbuf->TotalSize -= Len;
|
|
|
|
Index = (FromHead ? 0 : Nbuf->BlockOpNum - 1);
|
|
BlockOp = Nbuf->BlockOp;
|
|
|
|
for (;;) {
|
|
if (BlockOp[Index].Size == 0) {
|
|
Index += (FromHead ? 1 : -1);
|
|
continue;
|
|
}
|
|
|
|
if (Len > BlockOp[Index].Size) {
|
|
Len -= BlockOp[Index].Size;
|
|
Trimmed += BlockOp[Index].Size;
|
|
NetblockTrim (&BlockOp[Index], BlockOp[Index].Size, FromHead);
|
|
} else {
|
|
Trimmed += Len;
|
|
NetblockTrim (&BlockOp[Index], Len, FromHead);
|
|
break;
|
|
}
|
|
|
|
Index += (FromHead ? 1 : -1);
|
|
}
|
|
|
|
return Trimmed;
|
|
}
|
|
|
|
|
|
/**
|
|
Copy Len bytes of data from the specific offset of the net buffer to the
|
|
destination memory.
|
|
|
|
The Len bytes of data may cross the several fragments of the net buffer.
|
|
|
|
@param[in] Nbuf Pointer to the net buffer.
|
|
@param[in] Offset The sequence number of the first byte to copy.
|
|
@param[in] Len Length of the data to copy.
|
|
@param[in] Dest The destination of the data to copy to.
|
|
|
|
@return The length of the actual copied data, or 0 if the offset
|
|
specified exceeds the total size of net buffer.
|
|
|
|
**/
|
|
UINT32
|
|
EFIAPI
|
|
NetbufCopy (
|
|
IN NET_BUF *Nbuf,
|
|
IN UINT32 Offset,
|
|
IN UINT32 Len,
|
|
IN UINT8 *Dest
|
|
)
|
|
{
|
|
NET_BLOCK_OP *BlockOp;
|
|
UINT32 Skip;
|
|
UINT32 Left;
|
|
UINT32 Copied;
|
|
UINT32 Index;
|
|
UINT32 Cur;
|
|
|
|
NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
|
|
ASSERT (Dest);
|
|
|
|
if ((Len == 0) || (Nbuf->TotalSize <= Offset)) {
|
|
return 0;
|
|
}
|
|
|
|
if (Nbuf->TotalSize - Offset < Len) {
|
|
Len = Nbuf->TotalSize - Offset;
|
|
}
|
|
|
|
BlockOp = Nbuf->BlockOp;
|
|
|
|
//
|
|
// Skip to the offset. Don't make "Offset-By-One" error here.
|
|
// Cur + BLOCK.SIZE is the first sequence number of next block.
|
|
// So, (Offset < Cur + BLOCK.SIZE) means that the first byte
|
|
// is in the current block. if (Offset == Cur + BLOCK.SIZE), the
|
|
// first byte is the next block's first byte.
|
|
//
|
|
Cur = 0;
|
|
|
|
for (Index = 0; Index < Nbuf->BlockOpNum; Index++) {
|
|
if (BlockOp[Index].Size == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (Offset < Cur + BlockOp[Index].Size) {
|
|
break;
|
|
}
|
|
|
|
Cur += BlockOp[Index].Size;
|
|
}
|
|
|
|
//
|
|
// Cur is the sequence number of the first byte in the block
|
|
// Offset - Cur is the number of bytes before first byte to
|
|
// to copy in the current block.
|
|
//
|
|
Skip = Offset - Cur;
|
|
Left = BlockOp[Index].Size - Skip;
|
|
|
|
if (Len <= Left) {
|
|
CopyMem (Dest, BlockOp[Index].Head + Skip, Len);
|
|
return Len;
|
|
}
|
|
|
|
CopyMem (Dest, BlockOp[Index].Head + Skip, Left);
|
|
|
|
Dest += Left;
|
|
Len -= Left;
|
|
Copied = Left;
|
|
|
|
Index++;
|
|
|
|
for (; Index < Nbuf->BlockOpNum; Index++) {
|
|
if (Len > BlockOp[Index].Size) {
|
|
Len -= BlockOp[Index].Size;
|
|
Copied += BlockOp[Index].Size;
|
|
|
|
CopyMem (Dest, BlockOp[Index].Head, BlockOp[Index].Size);
|
|
Dest += BlockOp[Index].Size;
|
|
} else {
|
|
Copied += Len;
|
|
CopyMem (Dest, BlockOp[Index].Head, Len);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Copied;
|
|
}
|
|
|
|
|
|
/**
|
|
Initiate the net buffer queue.
|
|
|
|
@param[in, out] NbufQue Pointer to the net buffer queue to be initialized.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
NetbufQueInit (
|
|
IN OUT NET_BUF_QUEUE *NbufQue
|
|
)
|
|
{
|
|
NbufQue->Signature = NET_QUE_SIGNATURE;
|
|
NbufQue->RefCnt = 1;
|
|
InitializeListHead (&NbufQue->List);
|
|
|
|
InitializeListHead (&NbufQue->BufList);
|
|
NbufQue->BufSize = 0;
|
|
NbufQue->BufNum = 0;
|
|
}
|
|
|
|
|
|
/**
|
|
Allocate and initialize a net buffer queue.
|
|
|
|
@return Pointer to the allocated net buffer queue, or NULL if the
|
|
allocation failed due to resource limit.
|
|
|
|
**/
|
|
NET_BUF_QUEUE *
|
|
EFIAPI
|
|
NetbufQueAlloc (
|
|
VOID
|
|
)
|
|
{
|
|
NET_BUF_QUEUE *NbufQue;
|
|
|
|
NbufQue = AllocatePool (sizeof (NET_BUF_QUEUE));
|
|
if (NbufQue == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
NetbufQueInit (NbufQue);
|
|
|
|
return NbufQue;
|
|
}
|
|
|
|
|
|
/**
|
|
Free a net buffer queue.
|
|
|
|
Decrease the reference count of the net buffer queue by one. The real resource
|
|
free operation isn't performed until the reference count of the net buffer
|
|
queue is decreased to 0.
|
|
|
|
@param[in] NbufQue Pointer to the net buffer queue to be freed.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
NetbufQueFree (
|
|
IN NET_BUF_QUEUE *NbufQue
|
|
)
|
|
{
|
|
NET_CHECK_SIGNATURE (NbufQue, NET_QUE_SIGNATURE);
|
|
|
|
NbufQue->RefCnt--;
|
|
|
|
if (NbufQue->RefCnt == 0) {
|
|
NetbufQueFlush (NbufQue);
|
|
gBS->FreePool (NbufQue);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Append a net buffer to the net buffer queue.
|
|
|
|
@param[in, out] NbufQue Pointer to the net buffer queue.
|
|
@param[in, out] Nbuf Pointer to the net buffer to be appended.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
NetbufQueAppend (
|
|
IN OUT NET_BUF_QUEUE *NbufQue,
|
|
IN OUT NET_BUF *Nbuf
|
|
)
|
|
{
|
|
NET_CHECK_SIGNATURE (NbufQue, NET_QUE_SIGNATURE);
|
|
NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
|
|
|
|
InsertTailList (&NbufQue->BufList, &Nbuf->List);
|
|
|
|
NbufQue->BufSize += Nbuf->TotalSize;
|
|
NbufQue->BufNum++;
|
|
}
|
|
|
|
|
|
/**
|
|
Remove a net buffer from the head in the specific queue and return it.
|
|
|
|
@param[in, out] NbufQue Pointer to the net buffer queue.
|
|
|
|
@return Pointer to the net buffer removed from the specific queue,
|
|
or NULL if there is no net buffer in the specific queue.
|
|
|
|
**/
|
|
NET_BUF *
|
|
EFIAPI
|
|
NetbufQueRemove (
|
|
IN OUT NET_BUF_QUEUE *NbufQue
|
|
)
|
|
{
|
|
NET_BUF *First;
|
|
|
|
NET_CHECK_SIGNATURE (NbufQue, NET_QUE_SIGNATURE);
|
|
|
|
if (NbufQue->BufNum == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
First = NET_LIST_USER_STRUCT (NbufQue->BufList.ForwardLink, NET_BUF, List);
|
|
|
|
NetListRemoveHead (&NbufQue->BufList);
|
|
|
|
NbufQue->BufSize -= First->TotalSize;
|
|
NbufQue->BufNum--;
|
|
return First;
|
|
}
|
|
|
|
|
|
/**
|
|
Copy Len bytes of data from the net buffer queue at the specific offset to the
|
|
destination memory.
|
|
|
|
The copying operation is the same as NetbufCopy but applies to the net buffer
|
|
queue instead of the net buffer.
|
|
|
|
@param[in] NbufQue Pointer to the net buffer queue.
|
|
@param[in] Offset The sequence number of the first byte to copy.
|
|
@param[in] Len Length of the data to copy.
|
|
@param[out] Dest The destination of the data to copy to.
|
|
|
|
@return The length of the actual copied data, or 0 if the offset
|
|
specified exceeds the total size of net buffer queue.
|
|
|
|
**/
|
|
UINT32
|
|
EFIAPI
|
|
NetbufQueCopy (
|
|
IN NET_BUF_QUEUE *NbufQue,
|
|
IN UINT32 Offset,
|
|
IN UINT32 Len,
|
|
OUT UINT8 *Dest
|
|
)
|
|
{
|
|
LIST_ENTRY *Entry;
|
|
NET_BUF *Nbuf;
|
|
UINT32 Skip;
|
|
UINT32 Left;
|
|
UINT32 Cur;
|
|
UINT32 Copied;
|
|
|
|
NET_CHECK_SIGNATURE (NbufQue, NET_QUE_SIGNATURE);
|
|
ASSERT (Dest != NULL);
|
|
|
|
if ((Len == 0) || (NbufQue->BufSize <= Offset)) {
|
|
return 0;
|
|
}
|
|
|
|
if (NbufQue->BufSize - Offset < Len) {
|
|
Len = NbufQue->BufSize - Offset;
|
|
}
|
|
|
|
//
|
|
// skip to the Offset
|
|
//
|
|
Cur = 0;
|
|
Nbuf = NULL;
|
|
|
|
NET_LIST_FOR_EACH (Entry, &NbufQue->BufList) {
|
|
Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
|
|
|
|
if (Offset < Cur + Nbuf->TotalSize) {
|
|
break;
|
|
}
|
|
|
|
Cur += Nbuf->TotalSize;
|
|
}
|
|
|
|
ASSERT (Nbuf != NULL);
|
|
|
|
//
|
|
// Copy the data in the first buffer.
|
|
//
|
|
Skip = Offset - Cur;
|
|
Left = Nbuf->TotalSize - Skip;
|
|
|
|
if (Len < Left) {
|
|
return NetbufCopy (Nbuf, Skip, Len, Dest);
|
|
}
|
|
|
|
NetbufCopy (Nbuf, Skip, Left, Dest);
|
|
Dest += Left;
|
|
Len -= Left;
|
|
Copied = Left;
|
|
|
|
//
|
|
// Iterate over the others
|
|
//
|
|
Entry = Entry->ForwardLink;
|
|
|
|
while ((Len > 0) && (Entry != &NbufQue->BufList)) {
|
|
Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
|
|
|
|
if (Len > Nbuf->TotalSize) {
|
|
Len -= Nbuf->TotalSize;
|
|
Copied += Nbuf->TotalSize;
|
|
|
|
NetbufCopy (Nbuf, 0, Nbuf->TotalSize, Dest);
|
|
Dest += Nbuf->TotalSize;
|
|
|
|
} else {
|
|
NetbufCopy (Nbuf, 0, Len, Dest);
|
|
Copied += Len;
|
|
break;
|
|
}
|
|
|
|
Entry = Entry->ForwardLink;
|
|
}
|
|
|
|
return Copied;
|
|
}
|
|
|
|
|
|
/**
|
|
Trim Len bytes of data from the queue header, release any of the net buffer
|
|
whom is trimmed wholely.
|
|
|
|
The trimming operation is the same as NetbufTrim but applies to the net buffer
|
|
queue instead of the net buffer.
|
|
|
|
@param[in, out] NbufQue Pointer to the net buffer queue.
|
|
@param[in] Len Length of the data to trim.
|
|
|
|
@return The actual length of the data trimmed.
|
|
|
|
**/
|
|
UINT32
|
|
EFIAPI
|
|
NetbufQueTrim (
|
|
IN OUT NET_BUF_QUEUE *NbufQue,
|
|
IN UINT32 Len
|
|
)
|
|
{
|
|
LIST_ENTRY *Entry;
|
|
LIST_ENTRY *Next;
|
|
NET_BUF *Nbuf;
|
|
UINT32 Trimmed;
|
|
|
|
NET_CHECK_SIGNATURE (NbufQue, NET_QUE_SIGNATURE);
|
|
|
|
if (Len == 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (Len > NbufQue->BufSize) {
|
|
Len = NbufQue->BufSize;
|
|
}
|
|
|
|
NbufQue->BufSize -= Len;
|
|
Trimmed = 0;
|
|
|
|
NET_LIST_FOR_EACH_SAFE (Entry, Next, &NbufQue->BufList) {
|
|
Nbuf = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
|
|
|
|
if (Len >= Nbuf->TotalSize) {
|
|
Trimmed += Nbuf->TotalSize;
|
|
Len -= Nbuf->TotalSize;
|
|
|
|
RemoveEntryList (Entry);
|
|
NetbufFree (Nbuf);
|
|
|
|
NbufQue->BufNum--;
|
|
|
|
if (Len == 0) {
|
|
break;
|
|
}
|
|
|
|
} else {
|
|
Trimmed += NetbufTrim (Nbuf, Len, NET_BUF_HEAD);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Trimmed;
|
|
}
|
|
|
|
|
|
/**
|
|
Flush the net buffer queue.
|
|
|
|
@param[in, out] NbufQue Pointer to the queue to be flushed.
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
NetbufQueFlush (
|
|
IN OUT NET_BUF_QUEUE *NbufQue
|
|
)
|
|
{
|
|
NET_CHECK_SIGNATURE (NbufQue, NET_QUE_SIGNATURE);
|
|
|
|
NetbufFreeList (&NbufQue->BufList);
|
|
|
|
NbufQue->BufNum = 0;
|
|
NbufQue->BufSize = 0;
|
|
}
|
|
|
|
|
|
/**
|
|
Compute the checksum for a bulk of data.
|
|
|
|
@param[in] Bulk Pointer to the data.
|
|
@param[in] Len Length of the data, in bytes.
|
|
|
|
@return The computed checksum.
|
|
|
|
**/
|
|
UINT16
|
|
EFIAPI
|
|
NetblockChecksum (
|
|
IN UINT8 *Bulk,
|
|
IN UINT32 Len
|
|
)
|
|
{
|
|
register UINT32 Sum;
|
|
|
|
Sum = 0;
|
|
|
|
while (Len > 1) {
|
|
Sum += *(UINT16 *) Bulk;
|
|
Bulk += 2;
|
|
Len -= 2;
|
|
}
|
|
|
|
//
|
|
// Add left-over byte, if any
|
|
//
|
|
if (Len > 0) {
|
|
Sum += *(UINT8 *) Bulk;
|
|
}
|
|
|
|
//
|
|
// Fold 32-bit sum to 16 bits
|
|
//
|
|
while ((Sum >> 16) != 0) {
|
|
Sum = (Sum & 0xffff) + (Sum >> 16);
|
|
|
|
}
|
|
|
|
return (UINT16) Sum;
|
|
}
|
|
|
|
|
|
/**
|
|
Add two checksums.
|
|
|
|
@param[in] Checksum1 The first checksum to be added.
|
|
@param[in] Checksum2 The second checksum to be added.
|
|
|
|
@return The new checksum.
|
|
|
|
**/
|
|
UINT16
|
|
EFIAPI
|
|
NetAddChecksum (
|
|
IN UINT16 Checksum1,
|
|
IN UINT16 Checksum2
|
|
)
|
|
{
|
|
UINT32 Sum;
|
|
|
|
Sum = Checksum1 + Checksum2;
|
|
|
|
//
|
|
// two UINT16 can only add up to a carry of 1.
|
|
//
|
|
if ((Sum >> 16) != 0) {
|
|
Sum = (Sum & 0xffff) + 1;
|
|
|
|
}
|
|
|
|
return (UINT16) Sum;
|
|
}
|
|
|
|
|
|
/**
|
|
Compute the checksum for a NET_BUF.
|
|
|
|
@param[in] Nbuf Pointer to the net buffer.
|
|
|
|
@return The computed checksum.
|
|
|
|
**/
|
|
UINT16
|
|
EFIAPI
|
|
NetbufChecksum (
|
|
IN NET_BUF *Nbuf
|
|
)
|
|
{
|
|
NET_BLOCK_OP *BlockOp;
|
|
UINT32 Offset;
|
|
UINT16 TotalSum;
|
|
UINT16 BlockSum;
|
|
UINT32 Index;
|
|
|
|
NET_CHECK_SIGNATURE (Nbuf, NET_BUF_SIGNATURE);
|
|
|
|
TotalSum = 0;
|
|
Offset = 0;
|
|
BlockOp = Nbuf->BlockOp;
|
|
|
|
for (Index = 0; Index < Nbuf->BlockOpNum; Index++) {
|
|
if (BlockOp[Index].Size == 0) {
|
|
continue;
|
|
}
|
|
|
|
BlockSum = NetblockChecksum (BlockOp[Index].Head, BlockOp[Index].Size);
|
|
|
|
if ((Offset & 0x01) != 0) {
|
|
//
|
|
// The checksum starts with an odd byte, swap
|
|
// the checksum before added to total checksum
|
|
//
|
|
BlockSum = (UINT16) NET_SWAP_SHORT (BlockSum);
|
|
}
|
|
|
|
TotalSum = NetAddChecksum (BlockSum, TotalSum);
|
|
Offset += BlockOp[Index].Size;
|
|
}
|
|
|
|
return TotalSum;
|
|
}
|
|
|
|
|
|
/**
|
|
Compute the checksum for TCP/UDP pseudo header.
|
|
|
|
Src and Dst are in network byte order, and Len is in host byte order.
|
|
|
|
@param[in] Src The source address of the packet.
|
|
@param[in] Dst The destination address of the packet.
|
|
@param[in] Proto The protocol type of the packet.
|
|
@param[in] Len The length of the packet.
|
|
|
|
@return The computed checksum.
|
|
|
|
**/
|
|
UINT16
|
|
EFIAPI
|
|
NetPseudoHeadChecksum (
|
|
IN IP4_ADDR Src,
|
|
IN IP4_ADDR Dst,
|
|
IN UINT8 Proto,
|
|
IN UINT16 Len
|
|
)
|
|
{
|
|
NET_PSEUDO_HDR Hdr;
|
|
|
|
//
|
|
// Zero the memory to relieve align problems
|
|
//
|
|
ZeroMem (&Hdr, sizeof (Hdr));
|
|
|
|
Hdr.SrcIp = Src;
|
|
Hdr.DstIp = Dst;
|
|
Hdr.Protocol = Proto;
|
|
Hdr.Len = HTONS (Len);
|
|
|
|
return NetblockChecksum ((UINT8 *) &Hdr, sizeof (Hdr));
|
|
}
|