aboutsummaryrefslogtreecommitdiffstats
path: root/lib/lufa/Projects/Webserver/Lib/DHCPCommon.c
blob: 6d80f65caeffc4f6c9b8ce9fadd260bb09223898 (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
/*
             LUFA Library
     Copyright (C) Dean Camera, 2017.

  dean [at] fourwalledcubicle [dot] com
           www.lufa-lib.org
*/

/*
  Copyright 2017  Dean Camera (dean [at] fourwalledcubicle [dot] com)

  Permission to use, copy, modify, distribute, and sell this
  software and its documentation for any purpose is hereby granted
  without fee, provided that the above copyright notice appear in
  all copies and that both that the copyright notice and this
  permission notice and warranty disclaimer appear in supporting
  documentation, and that the name of the author not be used in
  advertising or publicity pertaining to distribution of the
  software without specific, written prior permission.

  The author disclaims all warranties with regard to this
  software, including all implied warranties of merchantability
  and fitness.  In no event shall the author be liable for any
  special, indirect or consequential damages or any damages
  whatsoever resulting from loss of use, data or profits, whether
  in an action of contract, negligence or other tortious action,
  arising out of or in connection with the use or performance of
  this software.
*/

/** \file
 *
 *  Common DHCP routines to manage DHCP packet data.
 */

#include "DHCPCommon.h"

#if defined(ENABLE_DHCP_CLIENT) || defined(ENABLE_DHCP_SERVER) || defined(__DOXYGEN__)

/** Sets the given DHCP option in the DHCP packet's option list. This automatically moves the
 *  end of options terminator past the new option in the options list.
 *
 *  \param[in,out] DHCPOptionList  Pointer to the start of the DHCP packet's options list
 *  \param[in]     Option          DHCP option to add to the list
 *  \param[in]     DataLen         Size in bytes of the option data to add
 *  \param[in]     OptionData      Buffer where the option's data is to be sourced from
 *
 *  \return Number of bytes added to the DHCP packet
 */
uint8_t DHCPCommon_SetOption(uint8_t* DHCPOptionList,
                             const uint8_t Option,
                             const uint8_t DataLen,
                             void* const OptionData)
{
	/* Skip through the DHCP options list until the terminator option is found */
	while (*DHCPOptionList != DHCP_OPTION_END)
	  DHCPOptionList += (DHCPOptionList[1] + 2);

	/* Overwrite the existing terminator with the new option, add a new terminator at the end of the list */
	DHCPOptionList[0] = Option;
	DHCPOptionList[1] = DataLen;
	memcpy(&DHCPOptionList[2], OptionData, DataLen);
	DHCPOptionList[2 + DataLen] = DHCP_OPTION_END;

	/* Calculate the total number of bytes added to the outgoing packet */
	return (2 + DataLen);
}

/** Retrieves the given option's data (if present) from the DHCP packet's options list.
 *
 *  \param[in,out] DHCPOptionList  Pointer to the start of the DHCP packet's options list
 *  \param[in]     Option          DHCP option to retrieve to the list
 *  \param[out]    Destination     Buffer where the option's data is to be written to if found
 *
 *  \return Boolean \c true if the option was found in the DHCP packet's options list, \c false otherwise
 */
bool DHCPCommon_GetOption(const uint8_t* DHCPOptionList,
                          const uint8_t Option,
                          void* const Destination)
{
	/* Look through the incoming DHCP packet's options list for the requested option */
	while (*DHCPOptionList != DHCP_OPTION_END)
	{
		/* Check if the current DHCP option in the packet is the one requested */
		if (DHCPOptionList[0] == Option)
		{
			/* Copy request option's data to the destination buffer */
			memcpy(Destination, &DHCPOptionList[2], DHCPOptionList[1]);

			/* Indicate that the requested option data was successfully retrieved */
			return true;
		}

		/* Skip to next DHCP option in the options list */
		DHCPOptionList += (DHCPOptionList[1] + 2);
	}

	/* Requested option not found in the incoming packet's DHCP options list */
	return false;
}

#endif
color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*
 *  Copyright (C) 2001  MandrakeSoft S.A.
 *
 *    MandrakeSoft S.A.
 *    43, rue d'Aboukir
 *    75002 Paris - France
 *    http://www.linux-mandrake.com/
 *    http://www.mandrakesoft.com/
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 *  Yunhong Jiang <yunhong.jiang@intel.com>
 *  Ported to xen by using virtual IRQ line.
 * 
 *  Copyright (C) 2007 VA Linux Systems Japan K.K.
 *  Isaku Yamahata <yamahata at valinux co jp>
 *  SMP support
 *  xen save/restore support
 */

#include <xen/config.h>
#include <xen/types.h>
#include <xen/mm.h>
#include <xen/xmalloc.h>
#include <xen/lib.h>
#include <xen/errno.h>
#include <public/hvm/ioreq.h>
#include <asm/vlsapic.h>
#include <asm/viosapic.h>
#include <asm/current.h>
#include <asm/event.h>
#include <asm/hvm/support.h>
#include <public/hvm/save.h>

static void viosapic_deliver(struct viosapic *viosapic, int irq)
{
    uint16_t dest = viosapic->redirtbl[irq].dest_id;
    uint8_t delivery_mode = viosapic->redirtbl[irq].delivery_mode;
    uint8_t vector = viosapic->redirtbl[irq].vector;

    ASSERT(spin_is_locked(&viosapic->lock));

    if (vlsapic_deliver_int(viosapic_domain (viosapic),
                            dest, delivery_mode, vector) < 0)
        gdprintk(XENLOG_WARNING,
                 "viosapic: can't deliver int %u to %u (dm=%u)\n",
                 vector, dest, delivery_mode);
}


static int iosapic_get_highest_irq(struct viosapic *viosapic)
{
    uint64_t irqs = viosapic->irr & ~viosapic->isr ;
   
    if (irqs)
        return ia64_fls(irqs);

    return -1;
}


/* XXX If level interrupt, use vector->irq table for performance */
static int get_redir_num(struct viosapic *viosapic, int vector)
{
    int i;

    ASSERT(spin_is_locked(&viosapic->lock));
    for ( i = 0; i < VIOSAPIC_NUM_PINS; i++ )
        if ( viosapic->redirtbl[i].vector == vector )
            return i;

    return -1;
}


static void service_iosapic(struct viosapic *viosapic)
{
    int irq;

    while ( (irq = iosapic_get_highest_irq(viosapic)) != -1 )
    {
        if ( viosapic->redirtbl[irq].trig_mode == SAPIC_LEVEL )
            viosapic->isr |= (1UL << irq);

        viosapic_deliver(viosapic, irq);

        viosapic->irr &= ~(1UL << irq);
    }
}


static void viosapic_update_EOI(struct viosapic *viosapic, int vector)
{
    int redir_num;

    spin_lock(&viosapic->lock);
    if ( (redir_num = get_redir_num(viosapic, vector)) == -1 )
    {
        spin_unlock(&viosapic->lock);
        gdprintk(XENLOG_WARNING, "Can't find redir item for %d EOI\n", vector);
        return;
    }

    if ( !test_and_clear_bit(redir_num, &viosapic->isr) )
    {
        spin_unlock(&viosapic->lock);
        if ( viosapic->redirtbl[redir_num].trig_mode == SAPIC_LEVEL )
            gdprintk(XENLOG_WARNING, "redir %d not set for %d EOI\n",
                     redir_num, vector);
        return;
    }
    service_iosapic(viosapic);
    spin_unlock(&viosapic->lock);
}


static unsigned long viosapic_read_indirect(struct viosapic *viosapic,
                                            unsigned long addr,
                                            unsigned long length)
{
    unsigned long result = 0;

    switch ( viosapic->ioregsel )
    {
    case VIOSAPIC_VERSION:
        result = ((((VIOSAPIC_NUM_PINS - 1) & 0xff) << 16)
                  | (VIOSAPIC_VERSION_ID & 0xff));
        break;

    default:
    {
        /* ioregsel might be written at the same time. copy it before use. */
        uint32_t ioregsel = viosapic->ioregsel;
        uint32_t redir_index;
        uint64_t redir_content;

        redir_index = (ioregsel - 0x10) >> 1;
        if ( redir_index >= VIOSAPIC_NUM_PINS )
        {
            gdprintk(XENLOG_WARNING, "viosapic_read_indirect:undefined "
                     "ioregsel %x\n", ioregsel);
            break;
        }

        redir_content = viosapic->redirtbl[redir_index].bits;
        result = (ioregsel & 0x1) ?
                 (redir_content >> 32) & 0xffffffff :
                 redir_content & 0xffffffff;
        break;
    }
    }

    return result;
}


unsigned long viosapic_read(struct vcpu *v,
                            unsigned long addr,
                            unsigned long length)
{
    struct viosapic *viosapic = vcpu_viosapic(v);
    uint32_t result;

    addr &= 0xff;

    switch ( addr )
    {
    case VIOSAPIC_REG_SELECT:
        result = viosapic->ioregsel;
        break;

    case VIOSAPIC_WINDOW:
        result = viosapic_read_indirect(viosapic, addr, length);
        break;

    default:
        result = 0;
        break;
    }

    return result;
}


static void viosapic_write_indirect(struct viosapic *viosapic,
                                    unsigned long addr,
                                    unsigned long length,
                                    unsigned long val)
{
    switch ( viosapic->ioregsel )
    {
    case VIOSAPIC_VERSION:
        /* Writes are ignored. */
        break;

    default:
    {
        /* ioregsel might be written at the same time. copy it before use. */
        uint32_t ioregsel = viosapic->ioregsel;
        uint32_t redir_index;
        uint64_t redir_content;

        redir_index = (ioregsel - 0x10) >> 1;
        if ( redir_index >= VIOSAPIC_NUM_PINS )
        {
            gdprintk(XENLOG_WARNING, "viosapic_write_indirect "
                     "error register %x\n", viosapic->ioregsel);
            break;
        }

        spin_lock(&viosapic->lock);
        redir_content = viosapic->redirtbl[redir_index].bits;

        if ( ioregsel & 0x1 )
        {
            redir_content = (((uint64_t)val & 0xffffffff) << 32) |
                            (redir_content & 0xffffffff);
        }
        else
        {
            redir_content = ((redir_content >> 32) << 32) |
                            (val & 0xffffffff);
        }
        viosapic->redirtbl[redir_index].bits = redir_content;
        spin_unlock(&viosapic->lock);
        break;
    }
    } /* switch */
}


void viosapic_write(struct vcpu *v,
                      unsigned long addr,
                      unsigned long length,
                      unsigned long val)
{
    struct viosapic *viosapic = vcpu_viosapic(v);

    addr &= 0xff;

    switch ( addr )
    {
    case VIOSAPIC_REG_SELECT:
        viosapic->ioregsel = val;
        break;

    case VIOSAPIC_WINDOW:
        viosapic_write_indirect(viosapic, addr, length, val);
        break;

    case VIOSAPIC_EOI:
        viosapic_update_EOI(viosapic, val);
        break;

    default:
        break;
    }
}


static void viosapic_reset(struct viosapic *viosapic)
{
    int i;

    memset(viosapic, 0, sizeof(*viosapic));

    for ( i = 0; i < VIOSAPIC_NUM_PINS; i++ )
    {
        viosapic->redirtbl[i].mask = 0x1;
    }
    spin_lock_init(&viosapic->lock);
}

void viosapic_set_irq(struct domain *d, int irq, int level)
{
    struct viosapic *viosapic = domain_viosapic(d);
    uint64_t bit;

    spin_lock(&viosapic->lock);
    if ( (irq < 0) || (irq >= VIOSAPIC_NUM_PINS) )
        goto out;

    if ( viosapic->redirtbl[irq].mask )
        goto out;

    bit = 1UL << irq;
    if ( viosapic->redirtbl[irq].trig_mode == SAPIC_LEVEL )
    {
        if ( level )
            viosapic->irr |= bit;
        else
            viosapic->irr &= ~bit;
    }
    else
    {
        if ( level )
            /* XXX No irr clear for edge interrupt */
            viosapic->irr |= bit;
    }

    service_iosapic(viosapic);
out:    
    spin_unlock(&viosapic->lock);
}

#define hvm_pci_intx_gsi(dev, intx)  \
    (((((dev) << 2) + ((dev) >> 3) + (intx)) & 31) + 16)
        

void viosapic_set_pci_irq(struct domain *d, int device, int intx, int level)
{
    int irq;
    irq = hvm_pci_intx_gsi(device, intx);

    viosapic_set_irq(d, irq, level);
}

void viosapic_init(struct domain *d)
{
    struct viosapic *viosapic = domain_viosapic(d);

    viosapic_reset(viosapic);

    viosapic->lowest_vcpu = NULL;
    
    viosapic->base_address = VIOSAPIC_DEFAULT_BASE_ADDRESS;
}

#define VIOSAPIC_INVALID_VCPU_ID (-1UL)
static int viosapic_save(struct domain *d, hvm_domain_context_t *h)
{
    struct viosapic *viosapic = domain_viosapic(d);
    struct hvm_hw_ia64_viosapic viosapic_save;
    int i;

    memset(&viosapic_save, 0, sizeof(viosapic_save));
    
    spin_lock(&viosapic->lock);
    viosapic_save.irr = viosapic->irr;
    viosapic_save.isr = viosapic->isr;
    viosapic_save.ioregsel = viosapic->ioregsel;
    if (viosapic->lowest_vcpu != NULL)
        viosapic_save.lowest_vcpu_id = viosapic->lowest_vcpu->vcpu_id;
    else
        viosapic_save.lowest_vcpu_id = VIOSAPIC_INVALID_VCPU_ID;
    viosapic_save.base_address = viosapic->base_address;

    for (i = 0; i < VIOSAPIC_NUM_PINS; i++)
        viosapic_save.redirtbl[i] = viosapic->redirtbl[i];
    spin_unlock(&viosapic->lock);

    return hvm_save_entry(VIOSAPIC, 0, h, &viosapic_save);
}

static int viosapic_load(struct domain *d, hvm_domain_context_t *h)
{
    struct viosapic *viosapic = domain_viosapic(d);
    struct hvm_hw_ia64_viosapic viosapic_load;
    struct vcpu *lowest_vcpu;
    int i;

    if (hvm_load_entry(VIOSAPIC, h, &viosapic_load))
        return -EINVAL;

    lowest_vcpu = NULL;
    if (viosapic_load.lowest_vcpu_id < MAX_VIRT_CPUS)
        lowest_vcpu = d->vcpu[viosapic_load.lowest_vcpu_id];
    else if (viosapic_load.lowest_vcpu_id != VIOSAPIC_INVALID_VCPU_ID)
        return -EINVAL;

    if (viosapic_load.base_address != VIOSAPIC_DEFAULT_BASE_ADDRESS)
        return -EINVAL;

    spin_lock(&viosapic->lock);
    viosapic->irr = viosapic_load.irr;
    viosapic->isr = viosapic_load.isr;
    viosapic->ioregsel = viosapic_load.ioregsel;

    viosapic->lowest_vcpu = lowest_vcpu;

    viosapic->base_address = viosapic_load.base_address;

    for (i = 0; i < VIOSAPIC_NUM_PINS; i++)
        viosapic->redirtbl[i] = viosapic_load.redirtbl[i];

    service_iosapic(viosapic);//XXX
    spin_unlock(&viosapic->lock);

    return 0;
}

HVM_REGISTER_SAVE_RESTORE(VIOSAPIC, viosapic_save, viosapic_load,
                          1, HVMSR_PER_DOM);