rename dir with correct year

This commit is contained in:
SkyFi Geek 2025-03-19 12:40:39 -07:00
parent 9c02be5a6c
commit 2eb07b25a1
79236 changed files with 32443970 additions and 0 deletions

31
2025-03-19/atl1c/Makefile Normal file
View file

@ -0,0 +1,31 @@
MODULE = x86_64/atl1c.ko \
powerpc/atl1c.ko 440/atl1c.ko e500/atl1c.ko e500-smp/atl1c.ko \
tile/atl1c.ko arm64/atl1c.ko arm/atl1c.ko
atl1c_FILES = atl1c.c
atl1c_PREFIX = drivers/net
ifneq ($(KERNELRELEASE),)
EXTRA_CFLAGS := $(CFLAGS)
define moddef
obj-m += $(1).o
ifneq ($(1).o, $(2))
$(1)-y := $(2)
EXTRA_CFLAGS += $($(1)_CFLAGS)
endif
endef
$(foreach m, $(MODULE), $(eval $(call moddef,$(m:.ko=),$($(m:.ko=)_FILES:.c=.o))))
EXTRA_CFLAGS := $(subst -I, -I$(src)/, $(EXTRA_CFLAGS))
else
KERNELDIR := /lib/modules/$$(uname -r)/build
all::
$(MAKE) -C $(KERNELDIR) M=$$(pwd) $@
endif

2896
2025-03-19/atl1c/atl1c.c Normal file

File diff suppressed because it is too large Load diff

1566
2025-03-19/atl1c/atl1c.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,39 @@
MODULE = bridge2.ko bridge2_netfilter.ko
bridge2_FILES = br_fdb.c br_input.c br_forward.c br_if.c \
br_device.c br_ioctl.c br.c br_nf_core.c fast_path.c \
br_multicast.c br_vlan.c br_netlink.c \
br_dhcp_snooping.c br_vlan_tunnel.c br_netlink_tunnel.c br_vlan_options.c \
br_mdb.c br_switchdev.c
bridge2_PREFIX = net/bridge
bridge2_CFLAGS += -O2 --param=max-inline-insns-auto=40
bridge2_netfilter_FILES = br_nf_core.c br_netfilter_hooks.c br_netfilter_ipv6.c
bridge2_netfilter_PREFIX = net/bridge
ifneq ($(KERNELRELEASE),)
EXTRA_CFLAGS := $(CFLAGS)
define moddef
obj-m += $(1).o
ifneq ($(1).o, $(2))
$(1)-y := $(2)
EXTRA_CFLAGS += $($(1)_CFLAGS)
endif
endef
$(foreach m, $(MODULE), $(eval $(call moddef,$(m:.ko=),$($(m:.ko=)_FILES:.c=.o))))
EXTRA_CFLAGS := $(subst -I, -I$(src)/, $(EXTRA_CFLAGS))
else
KERNELDIR := /lib/modules/$$(uname -r)/build
all::
$(MAKE) -C $(KERNELDIR) M=$$(pwd) $@
endif

412
2025-03-19/bridge2/br.c Normal file
View file

@ -0,0 +1,412 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Generic parts
* Linux ethernet bridge
*
* Authors:
* Lennert Buytenhek <buytenh@gnu.org>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
#include <linux/llc.h>
#include <net/llc.h>
#include <net/stp.h>
#include <net/switchdev.h>
#include "br_private.h"
int __init bridge_fast_path_init(void);
void __exit bridge_fast_path_exit(void);
#include <net/addrconf.h>
/*
* Handle changes in state of network devices enslaved to a bridge.
*
* Note: don't care about up/down if bridge itself is down, because
* port state is checked when bridge is brought up.
*/
static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr)
{
struct netlink_ext_ack *extack = netdev_notifier_info_to_extack(ptr);
struct netdev_notifier_pre_changeaddr_info *prechaddr_info;
struct net_device *dev = netdev_notifier_info_to_dev(ptr);
struct net_bridge_port *p;
struct net_bridge *br;
bool notified = false;
// bool changed_addr;
int err;
if (dev->priv_flags & IFF_EBRIDGE) {
err = br_vlan_bridge_event(dev, event, ptr);
if (err)
return notifier_from_errno(err);
if (event == NETDEV_REGISTER) {
// /* register of bridge completed, add sysfs entries */
// br_sysfs_addbr(dev);
return NOTIFY_DONE;
}
}
/* not a port of a bridge */
p = br_port_get_rtnl(dev);
if (!p)
return NOTIFY_DONE;
br = p->br;
switch (event) {
case NETDEV_CHANGEMTU:
// FIXME - do we really disable? Now it doesnt change if user has changed.
// br_mtu_auto_adjust(br);
break;
case NETDEV_PRE_CHANGEADDR:
if (br->dev->addr_assign_type == NET_ADDR_SET)
break;
prechaddr_info = ptr;
err = dev_pre_changeaddr_notify(br->dev,
prechaddr_info->dev_addr,
extack);
if (err)
return notifier_from_errno(err);
break;
case NETDEV_CHANGEADDR:
spin_lock_bh(&br->lock);
br_fdb_changeaddr(p, dev->dev_addr);
// changed_addr = br_stp_recalculate_bridge_id(br);
spin_unlock_bh(&br->lock);
// if (changed_addr)
// call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
break;
case NETDEV_CHANGE:
// FIXME? this could call br_stp_disable_port which affect backup and multicast
// br_port_carrier_check(p, &notified);
break;
case NETDEV_FEAT_CHANGE:
netdev_update_features(br->dev);
break;
case NETDEV_DOWN:
spin_lock_bh(&br->lock);
if (br->dev->flags & IFF_UP) {
// br_stp_disable_port(p);
// notified = true;
{
if (!rcu_access_pointer(p->backup_port))
br_fdb_delete_by_port(br, p, 0, 0);
br_multicast_disable_port(p);
}
}
spin_unlock_bh(&br->lock);
break;
case NETDEV_UP:
// if (netif_running(br->dev) && netif_oper_up(dev)) {
// spin_lock_bh(&br->lock);
// br_stp_enable_port(p);
// notified = true;
// spin_unlock_bh(&br->lock);
// }
break;
case NETDEV_UNREGISTER:
br_del_if(br, dev);
break;
case NETDEV_CHANGENAME:
// err = br_sysfs_renameif(p);
// if (err)
// return notifier_from_errno(err);
break;
case NETDEV_PRE_TYPE_CHANGE:
/* Forbid underlaying device to change its type. */
return NOTIFY_BAD;
case NETDEV_RESEND_IGMP:
/* Propagate to master device */
call_netdevice_notifiers(event, br->dev);
break;
}
if (event != NETDEV_UNREGISTER)
br_vlan_port_event(p, event);
/* Events that may cause spanning tree to refresh */
if (!notified && (event == NETDEV_CHANGEADDR || event == NETDEV_UP ||
event == NETDEV_CHANGE || event == NETDEV_DOWN))
br_ifinfo_notify(RTM_NEWLINK, NULL, p);
return NOTIFY_DONE;
}
static struct notifier_block br_device_notifier = {
.notifier_call = br_device_event
};
/* called with RTNL or RCU */
static int br_switchdev_event(struct notifier_block *unused,
unsigned long event, void *ptr)
{
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
struct net_bridge_port *p;
struct net_bridge *br;
struct switchdev_notifier_fdb_info *fdb_info;
int err = NOTIFY_DONE;
p = br_port_get_rtnl_rcu(dev);
if (!p)
goto out;
br = p->br;
switch (event) {
case SWITCHDEV_FDB_ADD_TO_BRIDGE:
fdb_info = ptr;
err = br_fdb_external_learn_add(br, p, fdb_info->addr,
fdb_info->vid, false);
if (err) {
err = notifier_from_errno(err);
break;
}
br_fdb_offloaded_set(br, p, fdb_info->addr,
fdb_info->vid, true);
break;
case SWITCHDEV_FDB_DEL_TO_BRIDGE:
fdb_info = ptr;
err = br_fdb_external_learn_del(br, p, fdb_info->addr,
fdb_info->vid, false);
if (err)
err = notifier_from_errno(err);
break;
case SWITCHDEV_FDB_OFFLOADED:
fdb_info = ptr;
br_fdb_offloaded_set(br, p, fdb_info->addr,
fdb_info->vid, fdb_info->offloaded);
break;
}
out:
return NOTIFY_DONE;
}
static struct notifier_block br_switchdev_notifier = {
.notifier_call = br_switchdev_event,
};
/* br_boolopt_toggle - change user-controlled boolean option
*
* @br: bridge device
* @opt: id of the option to change
* @on: new option value
* @extack: extack for error messages
*
* Changes the value of the respective boolean option to @on taking care of
* any internal option value mapping and configuration.
*/
int br_boolopt_toggle(struct net_bridge *br, enum br_boolopt_id opt, bool on,
struct netlink_ext_ack *extack)
{
switch (opt) {
case BR_BOOLOPT_NO_LL_LEARN:
br_opt_toggle(br, BROPT_NO_LL_LEARN, on);
break;
default:
/* shouldn't be called with unsupported options */
WARN_ON(1);
break;
}
return 0;
}
int br_boolopt_get(const struct net_bridge *br, enum br_boolopt_id opt)
{
switch (opt) {
case BR_BOOLOPT_NO_LL_LEARN:
return br_opt_get(br, BROPT_NO_LL_LEARN);
default:
/* shouldn't be called with unsupported options */
WARN_ON(1);
break;
}
return 0;
}
int br_boolopt_multi_toggle(struct net_bridge *br,
struct br_boolopt_multi *bm,
struct netlink_ext_ack *extack)
{
unsigned long bitmap = bm->optmask;
int err = 0;
int opt_id;
for_each_set_bit(opt_id, &bitmap, BR_BOOLOPT_MAX) {
bool on = !!(bm->optval & BIT(opt_id));
err = br_boolopt_toggle(br, opt_id, on, extack);
if (err) {
br_debug(br, "boolopt multi-toggle error: option: %d current: %d new: %d error: %d\n",
opt_id, br_boolopt_get(br, opt_id), on, err);
break;
}
}
return err;
}
void br_boolopt_multi_get(const struct net_bridge *br,
struct br_boolopt_multi *bm)
{
u32 optval = 0;
int opt_id;
for (opt_id = 0; opt_id < BR_BOOLOPT_MAX; opt_id++)
optval |= (br_boolopt_get(br, opt_id) << opt_id);
bm->optval = optval;
bm->optmask = GENMASK((BR_BOOLOPT_MAX - 1), 0);
}
/* private bridge options, controlled by the kernel */
void br_opt_toggle(struct net_bridge *br, enum net_bridge_opts opt, bool on)
{
bool cur = !!br_opt_get(br, opt);
br_debug(br, "toggle option: %d state: %d -> %d\n",
opt, cur, on);
if (cur == on)
return;
if (on)
set_bit(opt, &br->options);
else
clear_bit(opt, &br->options);
}
static void __net_exit br_net_exit(struct net *net)
{
struct net_device *dev;
LIST_HEAD(list);
rtnl_lock();
for_each_netdev(net, dev)
if (dev->priv_flags & IFF_EBRIDGE)
br_dev_delete(dev, &list);
unregister_netdevice_many(&list);
rtnl_unlock();
}
static struct pernet_operations br_net_ops = {
.exit = br_net_exit,
};
//static const struct stp_proto br_stp_proto = {
// .rcv = br_stp_rcv,
//};
static int __init br_init(void)
{
int err;
BUILD_BUG_ON(sizeof(struct br_input_skb_cb) > sizeof_field(struct sk_buff, cb));
// err = stp_proto_register(&br_stp_proto);
// if (err < 0) {
// pr_err("bridge: can't register sap for STP\n");
// return err;
// }
err = br_fdb_init();
if (err)
goto err_out;
err = register_pernet_subsys(&br_net_ops);
if (err)
goto err_out1;
err = br_nf_core_init();
if (err)
goto err_out2;
err = register_netdevice_notifier(&br_device_notifier);
if (err)
goto err_out3;
err = register_switchdev_notifier(&br_switchdev_notifier);
if (err)
goto err_out4;
err = br_netlink_init();
if (err)
goto err_out5;
brioctl_set(br_ioctl_deviceless_stub);
#if IS_ENABLED(CONFIG_ATM_LANE)
br_fdb_test_addr_hook = br_fdb_test_addr;
#endif
#if IS_MODULE(CONFIG_BRIDGE_NETFILTER)
pr_info("bridge: filtering via arp/ip/ip6tables is no longer available "
"by default. Update your scripts to load br_netfilter if you "
"need this.\n");
#endif
bridge_fast_path_init();
return 0;
err_out5:
unregister_switchdev_notifier(&br_switchdev_notifier);
err_out4:
unregister_netdevice_notifier(&br_device_notifier);
err_out3:
br_nf_core_fini();
err_out2:
unregister_pernet_subsys(&br_net_ops);
err_out1:
br_fdb_fini();
err_out:
// stp_proto_unregister(&br_stp_proto);
return err;
}
static void __exit br_deinit(void)
{
// stp_proto_unregister(&br_stp_proto);
bridge_fast_path_exit();
br_netlink_fini();
unregister_switchdev_notifier(&br_switchdev_notifier);
unregister_netdevice_notifier(&br_device_notifier);
brioctl_set(NULL);
unregister_pernet_subsys(&br_net_ops);
rcu_barrier(); /* Wait for completion of call_rcu()'s */
br_nf_core_fini();
#if IS_ENABLED(CONFIG_ATM_LANE)
br_fdb_test_addr_hook = NULL;
#endif
br_fdb_fini();
}
module_init(br_init)
module_exit(br_deinit)
MODULE_LICENSE("GPL");
MODULE_VERSION(BR_VERSION);
MODULE_ALIAS_RTNL_LINK("bridge");

View file

@ -0,0 +1,525 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Device handling code
* Linux ethernet bridge
*
* Authors:
* Lennert Buytenhek <buytenh@gnu.org>
*/
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/netpoll.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/list.h>
#include <linux/netfilter_bridge.h>
#include <linux/packet_hook.h>
#include <linux/uaccess.h>
#include "br_private.h"
#define COMMON_FEATURES (NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | \
NETIF_F_GSO_MASK | NETIF_F_HW_CSUM)
const struct nf_br_ops __rcu *nf_br_ops __read_mostly;
EXPORT_SYMBOL_GPL(nf_br_ops);
/* net device transmit always called with BH disabled */
netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct net_bridge *br = netdev_priv(dev);
struct net_bridge_fdb_entry *dst;
struct net_bridge_mdb_entry *mdst;
// struct pcpu_sw_netstats *brstats = this_cpu_ptr(br->stats);
const struct nf_br_ops *nf_ops;
u8 state = BR_STATE_FORWARDING;
const unsigned char *dest;
u16 vid = 0;
fast_path_sp_tx_inc(br->dev, skb->len);
rcu_read_lock();
nf_ops = rcu_dereference(nf_br_ops);
if (nf_ops && nf_ops->br_dev_xmit_hook(skb)) {
rcu_read_unlock();
return NETDEV_TX_OK;
}
// u64_stats_update_begin(&brstats->syncp);
// brstats->tx_packets++;
// brstats->tx_bytes += skb->len;
// u64_stats_update_end(&brstats->syncp);
br_switchdev_frame_unmark(skb);
BR_INPUT_SKB_CB(skb)->brdev = dev;
// BR_INPUT_SKB_CB(skb)->frag_max_size = 0;
BR_INPUT_SKB_CB(skb)->dhcp_rep = 0;
BR_INPUT_SKB_CB(skb)->dhcp_req = 0;
BR_INPUT_SKB_CB(skb)->igmp = 0;
BR_INPUT_SKB_CB(skb)->mrouters_only = 0;
BR_INPUT_SKB_CB(skb)->ll_mc = 0;
BR_INPUT_SKB_CB(skb)->src_port_hw_switched = 0;
skb_reset_mac_header(skb);
skb_pull(skb, ETH_HLEN);
if (!br_allowed_ingress(br, br_vlan_group_rcu(br), br->vlan_ingress, skb, &vid, &state))
goto out;
// if (IS_ENABLED(CONFIG_INET) &&
// (eth_hdr(skb)->h_proto == htons(ETH_P_ARP) ||
// eth_hdr(skb)->h_proto == htons(ETH_P_RARP)) &&
// br_opt_get(br, BROPT_NEIGH_SUPPRESS_ENABLED)) {
// br_do_proxy_suppress_arp(skb, br, vid, NULL);
// } else if (IS_ENABLED(CONFIG_IPV6) &&
// skb->protocol == htons(ETH_P_IPV6) &&
// br_opt_get(br, BROPT_NEIGH_SUPPRESS_ENABLED) &&
// pskb_may_pull(skb, sizeof(struct ipv6hdr) +
// sizeof(struct nd_msg)) &&
// ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
// struct nd_msg *msg, _msg;
// msg = br_is_nd_neigh_msg(skb, &_msg);
// if (msg)
// br_do_suppress_nd(skb, br, vid, NULL, msg);
// }
dest = eth_hdr(skb)->h_dest;
if (is_broadcast_ether_addr(dest)) {
br_flood(br, skb, BR_PKT_BROADCAST, false, true, BR_NOHORIZON, BR_NOSWITCH);
} else if (is_multicast_ether_addr(dest)) {
// if (unlikely(netpoll_tx_running(dev))) {
// br_flood(br, skb, BR_PKT_MULTICAST, false, true, BR_NOHORIZON, BR_NOSWITCH);
// goto out;
// }
if (br_multicast_rcv(br, NULL, skb, vid, false)) {
kfree_skb(skb);
goto out;
}
mdst = br_mdb_get(br, skb, vid);
if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
br_multicast_querier_exists(br, eth_hdr(skb)))
br_multicast_flood(mdst, skb, false, true, BR_NOHORIZON, BR_NOSWITCH);
else
br_flood(br, skb, BR_PKT_MULTICAST, false, true, BR_NOHORIZON, BR_NOSWITCH);
} else if ((dst = br_fdb_find_rcu(br, dest, vid)) != NULL) {
br_forward(dst->dst, skb, false, true, BR_NOHORIZON, BR_NOSWITCH);
} else {
br_flood(br, skb, BR_PKT_UNICAST, false, true, BR_NOHORIZON, BR_NOSWITCH);
}
out:
rcu_read_unlock();
return NETDEV_TX_OK;
}
static int br_dev_init(struct net_device *dev)
{
struct net_bridge *br = netdev_priv(dev);
int err;
// br->stats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
// if (!br->stats)
// return -ENOMEM;
err = br_fdb_hash_init(br);
if (err) {
// free_percpu(br->stats);
return err;
}
err = br_mdb_hash_init(br);
if (err) {
// free_percpu(br->stats);
br_fdb_hash_fini(br);
return err;
}
err = br_vlan_init(br);
if (err) {
// free_percpu(br->stats);
br_mdb_hash_fini(br);
br_fdb_hash_fini(br);
return err;
}
// err = br_multicast_init_stats(br);
// if (err) {
// free_percpu(br->stats);
// br_vlan_flush(br);
// br_mdb_hash_fini(br);
// br_fdb_hash_fini(br);
// }
return err;
}
static void br_dev_uninit(struct net_device *dev)
{
struct net_bridge *br = netdev_priv(dev);
br_multicast_dev_del(br);
// br_multicast_uninit_stats(br);
br_vlan_flush(br);
br_mdb_hash_fini(br);
br_fdb_hash_fini(br);
// free_percpu(br->stats);
}
static int br_dev_open(struct net_device *dev)
{
struct net_bridge *br = netdev_priv(dev);
netdev_update_features(dev);
netif_start_queue(dev);
// br_stp_enable_bridge(br);
{
mod_delayed_work(system_long_wq, &br->gc_work, HZ / 10);
}
br_multicast_open(br);
return 0;
}
static void br_dev_set_multicast_list(struct net_device *dev)
{
struct net_bridge *br = netdev_priv(dev);
struct netdev_hw_addr *ha = NULL;
struct net_bridge_vlan_group *vg = br_vlan_group(br);
br_update_fast_forward(br);
br_fdb_flush_bridge_local(br);
netdev_for_each_uc_addr(ha, dev) {
if (!vg) {
br_fdb_insert(br, NULL, ha->addr, 0);
}
}
}
static void br_dev_change_rx_flags(struct net_device *dev, int change)
{
if (change & IFF_PROMISC)
br_manage_promisc(netdev_priv(dev));
}
static int br_dev_stop(struct net_device *dev)
{
struct net_bridge *br = netdev_priv(dev);
// br_stp_disable_bridge(br);
{
cancel_delayed_work_sync(&br->gc_work);
}
br_multicast_stop(br);
netif_stop_queue(dev);
return 0;
}
//static void br_get_stats64(struct net_device *dev,
// struct rtnl_link_stats64 *stats)
//{
// struct net_bridge *br = netdev_priv(dev);
// struct pcpu_sw_netstats tmp, sum = { 0 };
// unsigned int cpu;
// for_each_possible_cpu(cpu) {
// unsigned int start;
// const struct pcpu_sw_netstats *bstats
// = per_cpu_ptr(br->stats, cpu);
// do {
// start = u64_stats_fetch_begin_irq(&bstats->syncp);
// memcpy(&tmp, bstats, sizeof(tmp));
// } while (u64_stats_fetch_retry_irq(&bstats->syncp, start));
// sum.tx_bytes += tmp.tx_bytes;
// sum.tx_packets += tmp.tx_packets;
// sum.rx_bytes += tmp.rx_bytes;
// sum.rx_packets += tmp.rx_packets;
// }
// stats->tx_bytes = sum.tx_bytes;
// stats->tx_packets = sum.tx_packets;
// stats->rx_bytes = sum.rx_bytes;
// stats->rx_packets = sum.rx_packets;
//}
static int br_change_mtu(struct net_device *dev, int new_mtu)
{
struct net_bridge *br = netdev_priv(dev);
dev->mtu = new_mtu;
/* this flag will be cleared if the MTU was automatically adjusted */
br_opt_toggle(br, BROPT_MTU_SET_BY_USER, true);
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
/* remember the MTU in the rtable for PMTU */
dst_metric_set(&br->fake_rtable.dst, RTAX_MTU, new_mtu);
#endif
return 0;
}
static int br_change_l2mtu(struct net_device *dev, int new_mtu)
{
dev->l2mtu = new_mtu;
return 0;
}
/* Allow setting mac address to any valid ethernet address. */
static int br_set_mac_address(struct net_device *dev, void *p)
{
struct net_bridge *br = netdev_priv(dev);
struct sockaddr *addr = p;
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
/* dev_set_mac_addr() can be called by a master device on bridge's
* NETDEV_UNREGISTER, but since it's being destroyed do nothing
*/
if (dev->reg_state != NETREG_REGISTERED)
return -EBUSY;
spin_lock_bh(&br->lock);
if (!ether_addr_equal(dev->dev_addr, addr->sa_data)) {
br_fdb_change_mac_address(br, addr->sa_data);
memcpy(br->dev->dev_addr, addr->sa_data, ETH_ALEN);
// /* Mac address will be changed in br_stp_change_bridge_id(). */
// br_stp_change_bridge_id(br, addr->sa_data);
}
spin_unlock_bh(&br->lock);
br_update_fast_forward(br);
return 0;
}
static void br_getinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
strlcpy(info->driver, "bridge", sizeof(info->driver));
strlcpy(info->version, BR_VERSION, sizeof(info->version));
strlcpy(info->fw_version, "N/A", sizeof(info->fw_version));
strlcpy(info->bus_info, "N/A", sizeof(info->bus_info));
}
static int br_get_link_ksettings(struct net_device *dev,
struct ethtool_link_ksettings *cmd)
{
struct net_bridge *br = netdev_priv(dev);
struct net_bridge_port *p;
cmd->base.duplex = DUPLEX_UNKNOWN;
cmd->base.port = PORT_OTHER;
cmd->base.speed = SPEED_UNKNOWN;
list_for_each_entry(p, &br->port_list, list) {
struct ethtool_link_ksettings ecmd;
struct net_device *pdev = p->dev;
if (!netif_running(pdev) || !netif_oper_up(pdev))
continue;
if (__ethtool_get_link_ksettings(pdev, &ecmd))
continue;
if (ecmd.base.speed == (__u32)SPEED_UNKNOWN)
continue;
if (cmd->base.speed == (__u32)SPEED_UNKNOWN ||
cmd->base.speed < ecmd.base.speed)
cmd->base.speed = ecmd.base.speed;
}
return 0;
}
static netdev_features_t br_fix_features(struct net_device *dev,
netdev_features_t features)
{
struct net_bridge *br = netdev_priv(dev);
return br_features_recompute(br, features);
}
#ifdef CONFIG_NET_POLL_CONTROLLER
static void br_poll_controller(struct net_device *br_dev)
{
}
static void br_netpoll_cleanup(struct net_device *dev)
{
struct net_bridge *br = netdev_priv(dev);
struct net_bridge_port *p;
list_for_each_entry(p, &br->port_list, list)
br_netpoll_disable(p);
}
static int __br_netpoll_enable(struct net_bridge_port *p)
{
struct netpoll *np;
int err;
np = kzalloc(sizeof(*p->np), GFP_KERNEL);
if (!np)
return -ENOMEM;
err = __netpoll_setup(np, p->dev);
if (err) {
kfree(np);
return err;
}
p->np = np;
return err;
}
int br_netpoll_enable(struct net_bridge_port *p)
{
if (!p->br->dev->npinfo)
return 0;
return __br_netpoll_enable(p);
}
static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni)
{
struct net_bridge *br = netdev_priv(dev);
struct net_bridge_port *p;
int err = 0;
list_for_each_entry(p, &br->port_list, list) {
if (!p->dev)
continue;
err = __br_netpoll_enable(p);
if (err)
goto fail;
}
out:
return err;
fail:
br_netpoll_cleanup(dev);
goto out;
}
void br_netpoll_disable(struct net_bridge_port *p)
{
struct netpoll *np = p->np;
if (!np)
return;
p->np = NULL;
__netpoll_free(np);
}
#endif
static int br_add_slave(struct net_device *dev, struct net_device *slave_dev,
struct netlink_ext_ack *extack)
{
struct net_bridge *br = netdev_priv(dev);
return br_add_if(br, slave_dev, extack);
}
static int br_del_slave(struct net_device *dev, struct net_device *slave_dev)
{
struct net_bridge *br = netdev_priv(dev);
return br_del_if(br, slave_dev);
}
static const struct ethtool_ops br_ethtool_ops = {
.get_drvinfo = br_getinfo,
.get_link = ethtool_op_get_link,
.get_link_ksettings = br_get_link_ksettings,
};
struct net_device_ops br_netdev_ops = {
.ndo_open = br_dev_open,
.ndo_stop = br_dev_stop,
.ndo_init = br_dev_init,
.ndo_uninit = br_dev_uninit,
.ndo_start_xmit = br_dev_xmit,
.ndo_get_stats64 = fast_path_get_stats64,
.ndo_set_mac_address = br_set_mac_address,
.ndo_set_rx_mode = br_dev_set_multicast_list,
.ndo_change_rx_flags = br_dev_change_rx_flags,
.ndo_change_mtu = br_change_mtu,
.ndo_change_l2mtu = br_change_l2mtu,
.ndo_do_ioctl = br_dev_ioctl,
#ifdef CONFIG_NET_POLL_CONTROLLER
.ndo_netpoll_setup = br_netpoll_setup,
.ndo_netpoll_cleanup = br_netpoll_cleanup,
.ndo_poll_controller = br_poll_controller,
#endif
.ndo_add_slave = br_add_slave,
.ndo_del_slave = br_del_slave,
.ndo_fix_features = br_fix_features,
.ndo_fdb_add = br_fdb_add,
.ndo_fdb_del = br_fdb_delete,
.ndo_fdb_dump = br_fdb_dump,
.ndo_fdb_get = br_fdb_get,
.ndo_bridge_getlink = br_getlink,
.ndo_bridge_setlink = br_setlink,
.ndo_bridge_dellink = br_dellink,
.ndo_features_check = passthru_features_check,
};
static struct device_type br_type = {
.name = "bridge",
};
void br_dev_setup(struct net_device *dev)
{
struct net_bridge *br = netdev_priv(dev);
eth_hw_addr_random(dev);
ether_setup(dev);
dev->netdev_ops = &br_netdev_ops;
dev->needs_free_netdev = true;
dev->ethtool_ops = &br_ethtool_ops;
SET_NETDEV_DEVTYPE(dev, &br_type);
dev->priv_flags = IFF_EBRIDGE | IFF_NO_QUEUE;
dev->features = COMMON_FEATURES | NETIF_F_LLTX | NETIF_F_NETNS_LOCAL |
NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_STAG_TX;
dev->hw_features = COMMON_FEATURES | NETIF_F_HW_VLAN_CTAG_TX |
NETIF_F_HW_VLAN_STAG_TX;
dev->vlan_features = COMMON_FEATURES;
br->dev = dev;
spin_lock_init(&br->lock);
INIT_LIST_HEAD(&br->port_list);
INIT_HLIST_HEAD(&br->fdb_list);
spin_lock_init(&br->hash_lock);
// br->bridge_id.prio[0] = 0x80;
// br->bridge_id.prio[1] = 0x00;
ether_addr_copy(br->group_addr, eth_stp_addr);
// br->stp_enabled = BR_NO_STP;
br->group_fwd_mask = BR_GROUPFWD_DEFAULT;
br->group_fwd_mask_required = BR_GROUPFWD_DEFAULT;
// br->designated_root = br->bridge_id;
// br->bridge_max_age = br->max_age = 20 * HZ;
// br->bridge_hello_time = br->hello_time = 2 * HZ;
// br->bridge_forward_delay = br->forward_delay = 15 * HZ;
// br->bridge_ageing_time = br->ageing_time = BR_DEFAULT_AGEING_TIME;
br->ageing_time = BR_DEFAULT_AGEING_TIME;
dev->max_mtu = ETH_MAX_MTU;
dev->l2mtu = 65535;
br_netfilter_rtable_init(br);
// br_stp_timer_init(br);
br_multicast_init(br);
INIT_DELAYED_WORK(&br->gc_work, br_fdb_cleanup);
}

View file

@ -0,0 +1,360 @@
#include <net/ip.h>
#include <net/udp.h>
#include <linux/ratelimit.h>
#include <linux/log.h>
#include "br_private.h"
struct dhcphdr {
unsigned char op;
unsigned char htype;
unsigned char hlen;
unsigned char hops;
unsigned xid;
unsigned short secs;
unsigned short flags;
unsigned ciaddr;
unsigned yiaddr;
unsigned siaddr;
unsigned giaddr;
unsigned char chaddr[16];
unsigned char sname[64];
unsigned char file[128];
unsigned char options[0];
};
struct opt {
unsigned char type;
unsigned char len;
unsigned char value[0];
};
enum {
BOOTREQUEST = 1,
BOOTREPLY = 2,
};
enum {
DHCP_HOST_NAME = 12,
DHCP_REQ_ADDR = 50,
DHCP_LEASE_TIME = 51,
DHCP_MSG_TYPE = 53,
DHCP_SERVER_ID = 54,
DHCP_CLIENT_IDENTIFIER = 61,
DHCP_RELAY_AGENT = 82,
};
enum {
DHCP_DISCOVER = 1,
DHCP_OFFER = 2,
DHCP_REQUEST = 3,
DHCP_ACK = 5,
DHCP_RELEASE = 7,
};
void destroy_info_opt(struct rcu_head *head)
{
struct net_dhcp_info_opt *info_opt =
container_of(head, struct net_dhcp_info_opt, rcu);
kfree(info_opt->circuit_id);
kfree(info_opt->remote_id);
kfree(info_opt);
}
static struct opt *find_opt(unsigned char *opts, unsigned char *end,
unsigned type) {
struct opt *c = (struct opt *) opts;
while (true) {
if ((unsigned char *) c + sizeof(struct opt) > end) {
if ((unsigned char *) c + 1 > end) break;
if (type == 255 && c->type == 255) return c;
break;
}
if (c->type == 255) {
if (type == 255) return c;
else break;
}
if (c->value + c->len > end) break;
if (c->type == type) return c;
c = (struct opt *) (c->value + c->len);
}
return 0;
}
static unsigned put_relay_agent_info(unsigned char *buf,
struct net_dhcp_info_opt *info,
unsigned circuit_id_len,
unsigned remote_id_len,
char *vid_str,
unsigned vid_len) {
struct opt *opt = (struct opt *) buf;
opt->type = 1;
opt->len = circuit_id_len + vid_len;
memcpy(opt->value, info->circuit_id, circuit_id_len);
if (vid_len) {
memcpy(opt->value + circuit_id_len, vid_str, vid_len);
}
opt = (struct opt *) (opt->value + opt->len);
opt->type = 2;
opt->len = remote_id_len;
memcpy(opt->value, info->remote_id, remote_id_len);
return opt->value + opt->len - buf;
}
static int br_add_info_opt(struct sk_buff *skb, struct net_dhcp_info_opt *info,
struct dhcphdr *dhcph, struct opt *r, u16 vid)
{
unsigned circuit_id_len = strlen(info->circuit_id);
unsigned remote_id_len = strlen(info->remote_id);
int opt_len = sizeof(struct opt) * 3 +
circuit_id_len + remote_id_len + 1;
unsigned vid_len = 0;
char vid_str[6] = {0, 0, 0, 0, 0, 0};
if (vid) {
vid_len = snprintf(vid_str, 6, ":%d", vid);
opt_len += vid_len;
}
int free = skb_tail_pointer(skb) - (unsigned char *) r;
if (free < opt_len) {
opt_len -= free;
if (!pskb_may_pull(skb, opt_len)) {
return -EINVAL;
}
__skb_put(skb, opt_len);
}
else {
opt_len = 0;
}
r->type = DHCP_RELAY_AGENT;
r->len = put_relay_agent_info(r->value, info, circuit_id_len,
remote_id_len, vid_str, vid_len);
unsigned char *e = r->value + r->len;
*e = 255;
return 0;
}
static void br_checksum_udp(struct sk_buff *skb, unsigned len)
{
struct iphdr *iph = ip_hdr(skb);
struct udphdr *udph = udp_hdr(skb);
udph->len = htons(len);
udph->check = 0;
__wsum csum = csum_partial(udph, len, 0);
udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, len,
iph->protocol, csum);
if (udph->check == 0)
udph->check = CSUM_MANGLED_0;
len += ip_hdrlen(skb);
iph->tot_len = htons(len);
iph->check = 0;
iph->check = ip_fast_csum((u8 *)iph, iph->ihl);
}
static int br_check_udp(struct net_bridge *br, struct net_bridge_port *port,
struct sk_buff *skb, u16 vid)
{
struct udphdr *udph;
struct dhcphdr *dhcph;
unsigned int len;
int ret = -EINVAL;
if (!pskb_may_pull(skb, sizeof(struct udphdr)))
return -EINVAL;
udph = udp_hdr(skb);
if (udph->source != htons(67) && udph->dest != htons(67))
return 0;
len = ntohs(udph->len);
if (skb->len < len || len < sizeof(struct dhcphdr))
return -EINVAL;
if (!pskb_may_pull(skb, sizeof(*dhcph)))
return -EINVAL;
dhcph = (struct dhcphdr *) (skb_transport_header(skb) + sizeof(*udph));
if (dhcph->op == BOOTREPLY && !(port->flags & BR_TRUSTED_PORT)) {
if (__ratelimit(&br_warn_ratelimit_state)) {
log_message(bridge_warning_topics,
"*%08x: received DHCP server message "
"on untrusted port from source IP %pI4, "
"MAC %pM",
port->dev->ifindex,
&ip_hdr(skb)->saddr,
eth_hdr(skb)->h_source);
}
return -EINVAL;
}
unsigned char *end = skb_tail_pointer(skb);
unsigned char *opts = dhcph->options;
if (opts + 4 > end || get_unaligned((u32 *) opts) != htonl(0x63825363))
return 0;
opts += 4;
struct opt *type = find_opt(opts, end, DHCP_MSG_TYPE);
if (!type) {
if (dhcph->op == BOOTREQUEST) {
BR_INPUT_SKB_CB(skb)->dhcp_req = 1;
}
else if (dhcph->op == BOOTREPLY) {
BR_INPUT_SKB_CB(skb)->dhcp_rep = 1;
}
else {
return -EINVAL;
}
return 0;
}
if (!type || type->len != 1)
return -EINVAL;
struct opt *eopt = find_opt(opts, end, 255);
if (!eopt)
return -EINVAL;
if (type->value[0] == DHCP_DISCOVER || type->value[0] == DHCP_REQUEST ||
type->value[0] == DHCP_RELEASE) {
struct opt *agent = find_opt(opts, end, DHCP_RELAY_AGENT);
if (!(port->flags & BR_TRUSTED_PORT) && agent)
return -EINVAL;
BR_INPUT_SKB_CB(skb)->dhcp_req = 1;
if (br->add_info_option && !agent) {
struct net_dhcp_info_opt *info;
info = rcu_dereference(port->info_option);
if (info) {
ret = br_add_info_opt(skb, info, dhcph, eopt, vid);
if (ret < 0)
return ret;
br_checksum_udp(skb, skb->len);
}
}
ret = 0;
} else if (type->value[0] == DHCP_OFFER || type->value[0] == DHCP_ACK) {
BR_INPUT_SKB_CB(skb)->dhcp_rep = 1;
ret = 0;
}
return ret;
}
struct sk_buff *br_dhcp_remove_agent_info(struct sk_buff *skb)
{
struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC);
if (!skb2) {
kfree_skb(skb);
return NULL;
}
kfree_skb(skb);
skb = skb2;
struct dhcphdr *dhcph = (struct dhcphdr *)
(skb_transport_header(skb) + sizeof(struct udphdr));
unsigned char *end = skb_tail_pointer(skb);
unsigned char *opts = dhcph->options;
opts += 4;
struct opt *agent = find_opt(opts, end, DHCP_RELAY_AGENT);
if (!agent)
return skb;
unsigned char *ptr = (unsigned char *) agent;
unsigned agent_len = sizeof(struct opt) + agent->len;
unsigned pkt_len = end - (unsigned char *) dhcph;
memmove(ptr, ptr + agent_len, end - ptr - agent_len);
struct opt *eopt = find_opt(opts, end, 255);
if (eopt) {
ptr = (unsigned char *) eopt;
++ptr;
if (ptr >= end) {
goto out;
}
if (pkt_len > 300 && pkt_len - agent_len < 300) {
/* trim to minimum dhcp packet size */
skb_trim(skb, skb->len - (pkt_len - 300));
end = skb_tail_pointer(skb);
}
if (ptr < end) {
memset(ptr, 0, end - ptr);
}
}
out:
br_checksum_udp(skb2, end - skb_transport_header(skb));
return skb2;
}
int br_dhcp_rcv(struct net_bridge *br, struct net_bridge_port *port,
struct sk_buff *skb, u16 vid)
{
struct sk_buff *skb2 = skb;
struct iphdr *iph;
int len;
unsigned offset;
int ret;
BR_INPUT_SKB_CB(skb)->dhcp_req = 0;
BR_INPUT_SKB_CB(skb)->dhcp_rep = 0;
if (!br->dhcp_snooping)
return 0;
if (skb->protocol != htons(ETH_P_IP))
return 0;
/* We treat OOM as packet loss for now. */
if (!pskb_may_pull(skb, sizeof(*iph)))
return -EINVAL;
iph = ip_hdr(skb);
if (iph->ihl < 5 || iph->version != 4)
return -EINVAL;
if (!pskb_may_pull(skb, ip_hdrlen(skb)))
return -EINVAL;
iph = ip_hdr(skb);
if (unlikely(ip_fast_csum(iph, iph->ihl)))
return -EINVAL;
if (iph->protocol != IPPROTO_UDP)
return 0;
len = ntohs(iph->tot_len);
if (skb->len < len || len < ip_hdrlen(skb))
return -EINVAL;
if (skb->len > len) {
skb2 = skb_clone(skb, GFP_ATOMIC);
if (!skb2)
return -ENOMEM;
ret = pskb_trim_rcsum(skb2, len);
if (ret)
goto err_out;
}
len -= ip_hdrlen(skb2);
offset = skb_network_offset(skb2) + ip_hdrlen(skb2);
__skb_pull(skb2, offset);
skb_reset_transport_header(skb2);
ret = br_check_udp(br, port, skb2, vid);
__skb_push(skb2, offset);
err_out:
if (skb2 != skb)
kfree_skb(skb2);
return ret;
}

1237
2025-03-19/bridge2/br_fdb.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,438 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Forwarding decision
* Linux ethernet bridge
*
* Authors:
* Lennert Buytenhek <buytenh@gnu.org>
*/
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/netpoll.h>
#include <linux/skbuff.h>
#include <linux/if_vlan.h>
#include <linux/netfilter_bridge.h>
#include <linux/switch.h>
#include "br_private.h"
#include "../mesh/mesh.h"
DECLARE_PER_CPU(unsigned, per_cpu_xmit_switched);
static inline bool is_hw_pkt(const struct sk_buff *skb,
const struct net_bridge_port *p)
{
return !br_multicast_igmp_type(skb) &&
!BR_INPUT_SKB_CB(skb)->dhcp_req &&
!BR_INPUT_SKB_CB(skb)->dhcp_rep &&
!(BR_INPUT_SKB_CB(skb)->ll_mc && hlist_unhashed(&p->rlist));
}
static inline int check_hw(const struct sk_buff *skb,
const struct net_bridge_port *p,
unsigned from_switch_group,
unsigned *switches)
{
if (p->switch_group == BR_NOSWITCH)
return true;
if (!is_hw_pkt(skb, p))
return true;
if (!switches && p->switch_group != from_switch_group)
return true;
if (switches && !(*switches & BIT(p->switch_group)) &&
p->switch_group != from_switch_group) {
*switches |= BIT(p->switch_group);
return true;
}
return false;
}
/* Don't forward packets to originating port or forwarding disabled */
static inline int should_deliver(const struct net_bridge_port *p,
const struct sk_buff *skb, unsigned horizon)
{
struct net_bridge_vlan_group *vg;
vg = nbp_vlan_group_rcu(p);
return ((p->flags & BR_HAIRPIN_MODE) || skb->dev != p->dev) &&
p->state == BR_STATE_FORWARDING && br_allowed_egress(vg, skb) &&
nbp_switchdev_allowed_egress(p, skb) &&
!br_skb_isolated(p, skb) && (horizon == BR_NOHORIZON || horizon != p->horizon)
&& (BR_INPUT_SKB_CB(skb)->src_port_peer_link
? !(p->flags & BR_MLAG_DUAL_LINK) : true);
}
static inline unsigned packet_length(const struct sk_buff *skb)
{
if (skb->protocol == htons(MESH_ENCAP_PROTO)) return 0;
return skb->len - (ANY_VLAN_PROTO_N(skb->protocol) ? VLAN_HLEN : 0);
}
static inline void set_priority(struct sk_buff *skb)
{
if (ANY_VLAN_PROTO_N(skb->protocol)) {
struct vlan_ethhdr *v = (struct vlan_ethhdr *)(skb->data - ETH_HLEN);
unsigned tci = ntohs(v->h_vlan_TCI);
tci = (tci & ~(7 << 13)) | ((skb->priority & 0x7) << 13);
v->h_vlan_TCI = htons(tci);
}
}
int br_dev_queue_push_xmit(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct net_bridge_port *port = br_port_get_rcu(skb->dev);
#if 0 // is_skb_forwardable is handled below - IFF_UP check and our l2mtu, mtu checks
if (!is_skb_forwardable(skb->dev, skb))
goto drop;
#endif
if (!(skb->dev->flags & IFF_UP))
goto drop;
/* drop mtu oversized packets except gso */
if (skb->dev->l2mtu) {
unsigned len = skb->len + (skb_vlan_tag_present(skb) ? VLAN_HLEN : 0);
if (skb->dev->l2mtu < len && !skb_is_gso(skb))
goto drop;
}
else if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb)) {
goto drop;
}
set_priority(skb);
skb_push(skb, ETH_HLEN);
br_drop_fake_rtable(skb);
if (skb->ip_summed == CHECKSUM_PARTIAL &&
(skb->protocol == htons(ETH_P_8021Q) ||
skb->protocol == htons(ETH_P_8021AD))) {
int depth;
if (!__vlan_get_protocol(skb, skb->protocol, &depth))
goto drop;
skb_set_network_header(skb, depth);
}
if (port && port->switch_group != BR_NOSWITCH && is_hw_pkt(skb, port)) {
skb = br_handle_vlan_switch(port->br, skb);
if (!skb)
return 0;
// This should get reset in the switch port xmit
*raw_cpu_ptr(&per_cpu_xmit_switched) = 1;
}
if (dev_queue_xmit(skb)) {
*raw_cpu_ptr(&per_cpu_xmit_switched) = 0;
}
return 0;
drop:
kfree_skb(skb);
return 0;
}
EXPORT_SYMBOL_GPL(br_dev_queue_push_xmit);
int br_forward_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
skb->tstamp = 0;
return NF_HOOK(NFPROTO_BRIDGE, NF_BR_POST_ROUTING,
net, sk, skb, NULL, skb->dev,
br_dev_queue_push_xmit);
}
EXPORT_SYMBOL_GPL(br_forward_finish);
static void __br_forward(const struct net_bridge_port *to,
struct sk_buff *skb, bool local_orig)
{
struct net_bridge_vlan_group *vg;
struct net_device *indev;
struct net *net;
int br_hook;
if (BR_INPUT_SKB_CB(skb)->dhcp_req && !(to->flags & BR_TRUSTED_PORT)) {
kfree_skb(skb);
return;
}
if (!is_hw_pkt(skb, to) || to->switch_group == BR_NOSWITCH) {
vg = nbp_vlan_group_rcu(to);
skb = br_handle_vlan(to->br, to, vg, skb);
if (!skb)
return;
}
if (to->br->add_info_option && !(to->flags & BR_TRUSTED_PORT) &&
BR_INPUT_SKB_CB(skb)->dhcp_rep) {
skb = br_dhcp_remove_agent_info(skb);
if (!skb)
return;
}
indev = skb->dev;
skb->dev = to->dev;
if (!local_orig) {
if (skb_warn_if_lro(skb)) {
kfree_skb(skb);
return;
}
br_hook = NF_BR_FORWARD;
skb_forward_csum(skb);
net = dev_net(indev);
} else {
#if 0
if (unlikely(netpoll_tx_running(to->br->dev))) {
if (!is_skb_forwardable(skb->dev, skb)) {
kfree_skb(skb);
} else {
skb_push(skb, ETH_HLEN);
br_netpoll_send_skb(to, skb);
}
return;
}
#endif
br_hook = NF_BR_LOCAL_OUT;
net = dev_net(skb->dev);
indev = NULL;
}
NF_HOOK(NFPROTO_BRIDGE, br_hook,
net, NULL, skb, indev, skb->dev,
br_forward_finish);
}
static int deliver_clone(const struct net_bridge_port *prev,
struct sk_buff *skb, bool local_orig)
{
struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev;
skb = skb_clone(skb, GFP_ATOMIC);
if (!skb) {
dev->stats.tx_dropped++;
return -ENOMEM;
}
__br_forward(prev, skb, local_orig);
return 0;
}
/**
* br_forward - forward a packet to a specific port
* @to: destination port
* @skb: packet being forwarded
* @local_rcv: packet will be received locally after forwarding
* @local_orig: packet is locally originated
*
* Should be called with rcu_read_lock.
*/
void br_forward(const struct net_bridge_port *to,
struct sk_buff *skb, bool local_rcv, bool local_orig,
unsigned horizon, unsigned switch_group)
{
if (unlikely(!to))
goto out;
/* redirect to backup link if the destination port is down */
if (rcu_access_pointer(to->backup_port) && !netif_carrier_ok(to->dev)) {
struct net_bridge_port *backup_port;
backup_port = rcu_dereference(to->backup_port);
if (unlikely(!backup_port))
goto out;
to = backup_port;
}
if (should_deliver(to, skb, horizon) &&
check_hw(skb, to, switch_group, NULL)) {
if (local_rcv)
deliver_clone(to, skb, local_orig);
else
__br_forward(to, skb, local_orig);
return;
}
out:
if (!local_rcv)
kfree_skb(skb);
}
EXPORT_SYMBOL(br_forward);
static struct net_bridge_port *maybe_deliver(
struct net_bridge_port *prev, struct net_bridge_port *p,
struct sk_buff *skb, bool local_orig,
unsigned horizon, unsigned from_switch_group, unsigned *switches)
{
u8 igmp_type = br_multicast_igmp_type(skb);
int err;
if (!should_deliver(p, skb, horizon))
return prev;
if (BR_INPUT_SKB_CB(skb)->dhcp_req && !(p->flags & BR_TRUSTED_PORT))
return prev;
if (!check_hw(skb, p, from_switch_group, switches))
return prev;
if (!prev)
goto out;
err = deliver_clone(prev, skb, local_orig);
if (err)
return ERR_PTR(err);
out:
br_multicast_count(p->br, p, skb, igmp_type, BR_MCAST_DIR_TX);
return p;
}
/* called under rcu_read_lock */
void br_flood(struct net_bridge *br, struct sk_buff *skb,
enum br_pkt_type pkt_type, bool local_rcv, bool local_orig,
unsigned horizon, unsigned switch_group)
{
struct net_bridge_port *prev = NULL;
struct net_bridge_port *p;
unsigned switches = 0;
list_for_each_entry_rcu(p, &br->port_list, list) {
/* Do not flood unicast traffic to ports that turn it off, nor
* other traffic if flood off, except for traffic we originate
*/
switch (pkt_type) {
case BR_PKT_UNICAST:
if (!(p->flags & BR_FLOOD))
continue;
break;
case BR_PKT_MULTICAST:
if (!(p->flags & BR_MCAST_FLOOD) && skb->dev != br->dev)
continue;
break;
case BR_PKT_BROADCAST:
if (!(p->flags & BR_BCAST_FLOOD) && skb->dev != br->dev)
continue;
break;
}
// /* Do not flood to ports that enable proxy ARP */
// if (p->flags & BR_PROXYARP)
// continue;
// if ((p->flags & (BR_PROXYARP_WIFI | BR_NEIGH_SUPPRESS)) &&
// BR_INPUT_SKB_CB(skb)->proxyarp_replied)
// continue;
prev = maybe_deliver(prev, p, skb, local_orig, horizon, switch_group, &switches);
if (IS_ERR(prev))
goto out;
}
if (!prev)
goto out;
if (local_rcv)
deliver_clone(prev, skb, local_orig);
else
__br_forward(prev, skb, local_orig);
return;
out:
if (!local_rcv)
kfree_skb(skb);
}
#ifdef CONFIG_BRIDGE_IGMP_SNOOPING
static void maybe_deliver_addr(struct net_bridge_port *p, struct sk_buff *skb,
const unsigned char *addr, bool local_orig,
unsigned horizon, unsigned from_switch_group,
unsigned *switches)
{
struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev;
const unsigned char *src = eth_hdr(skb)->h_source;
if (!should_deliver(p, skb, horizon))
return;
if (!check_hw(skb, p, from_switch_group, switches))
return;
/* Even with hairpin, no soliloquies - prevent breaking IPv6 DAD */
if (skb->dev == p->dev && ether_addr_equal(src, addr))
return;
skb = skb_copy(skb, GFP_ATOMIC);
if (!skb) {
dev->stats.tx_dropped++;
return;
}
if (!is_broadcast_ether_addr(addr))
memcpy(eth_hdr(skb)->h_dest, addr, ETH_ALEN);
__br_forward(p, skb, local_orig);
}
/* called with rcu_read_lock */
void br_multicast_flood(struct net_bridge_mdb_entry *mdst,
struct sk_buff *skb,
bool local_rcv, bool local_orig,
unsigned horizon, unsigned switch_group)
{
struct net_device *dev = BR_INPUT_SKB_CB(skb)->brdev;
struct net_bridge *br = netdev_priv(dev);
struct net_bridge_port *prev = NULL;
struct net_bridge_port_group *p;
struct hlist_node *rp;
unsigned switches = 0;
rp = rcu_dereference(hlist_first_rcu(&br->router_list));
p = mdst ? rcu_dereference(mdst->ports) : NULL;
while (p || rp) {
struct net_bridge_port *port, *lport, *rport;
lport = p ? p->port : NULL;
rport = hlist_entry_safe(rp, struct net_bridge_port, rlist);
if ((unsigned long)lport > (unsigned long)rport) {
port = lport;
if (port->flags & BR_MULTICAST_TO_UNICAST) {
maybe_deliver_addr(lport, skb, p->eth_addr,
local_orig,
horizon, switch_group, &switches);
goto delivered;
}
} else {
port = rport;
}
prev = maybe_deliver(prev, port, skb, local_orig,
horizon, switch_group, &switches);
if (IS_ERR(prev))
goto out;
delivered:
if ((unsigned long)lport >= (unsigned long)port)
p = rcu_dereference(p->next);
if ((unsigned long)rport >= (unsigned long)port)
rp = rcu_dereference(hlist_next_rcu(rp));
}
if (!prev)
goto out;
if (local_rcv)
deliver_clone(prev, skb, local_orig);
else
__br_forward(prev, skb, local_orig);
return;
out:
if (!local_rcv)
kfree_skb(skb);
}
#endif

850
2025-03-19/bridge2/br_if.c Normal file
View file

@ -0,0 +1,850 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Userspace interface
* Linux ethernet bridge
*
* Authors:
* Lennert Buytenhek <buytenh@gnu.org>
*/
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/netpoll.h>
#include <linux/ethtool.h>
#include <linux/if_arp.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rtnetlink.h>
#include <linux/if_ether.h>
#include <linux/slab.h>
#include <net/dsa.h>
#include <net/sock.h>
#include <linux/if_vlan.h>
#include <net/switchdev.h>
#include <net/net_namespace.h>
#include <linux/packet_hook.h>
#include "br_private.h"
extern struct fast_path bridge_fast_path;
extern struct fast_path bridge_fast_forward;
///*
// * Determine initial path cost based on speed.
// * using recommendations from 802.1d standard
// *
// * Since driver might sleep need to not be holding any locks.
// */
//static int port_cost(struct net_device *dev)
//{
// struct ethtool_link_ksettings ecmd;
// if (!__ethtool_get_link_ksettings(dev, &ecmd)) {
// switch (ecmd.base.speed) {
// case SPEED_10000:
// return 2;
// case SPEED_1000:
// return 4;
// case SPEED_100:
// return 19;
// case SPEED_10:
// return 100;
// }
// }
// /* Old silly heuristics based on name */
// if (!strncmp(dev->name, "lec", 3))
// return 7;
// if (!strncmp(dev->name, "plip", 4))
// return 2500;
// return 100; /* assume old 10Mbps */
//}
///* Check for port carrier transitions. */
//void br_port_carrier_check(struct net_bridge_port *p, bool *notified)
//{
// struct net_device *dev = p->dev;
// struct net_bridge *br = p->br;
// if (!(p->flags & BR_ADMIN_COST) &&
// netif_running(dev) && netif_oper_up(dev))
// p->path_cost = port_cost(dev);
// *notified = false;
// if (!netif_running(br->dev))
// return;
// spin_lock_bh(&br->lock);
// if (netif_running(dev) && netif_oper_up(dev)) {
// if (p->state == BR_STATE_DISABLED) {
// br_stp_enable_port(p);
// *notified = true;
// }
// } else {
// if (p->state != BR_STATE_DISABLED) {
// br_stp_disable_port(p);
// *notified = true;
// }
// }
// spin_unlock_bh(&br->lock);
//}
static void br_port_set_promisc(struct net_bridge_port *p)
{
int err = 0;
if (br_promisc_port(p))
return;
err = dev_set_promiscuity(p->dev, 1);
if (err)
return;
br_fdb_unsync_static(p->br, p);
p->flags |= BR_PROMISC;
}
static void br_port_clear_promisc(struct net_bridge_port *p)
{
int err;
/* Check if the port is already non-promisc or if it doesn't
* support UNICAST filtering. Without unicast filtering support
* we'll end up re-enabling promisc mode anyway, so just check for
* it here.
*/
if (!br_promisc_port(p) || !(p->dev->priv_flags & IFF_UNICAST_FLT))
return;
/* Since we'll be clearing the promisc mode, program the port
* first so that we don't have interruption in traffic.
*/
err = br_fdb_sync_static(p->br, p);
if (err)
return;
dev_set_promiscuity(p->dev, -1);
p->flags &= ~BR_PROMISC;
}
/* When a port is added or removed or when certain port flags
* change, this function is called to automatically manage
* promiscuity setting of all the bridge ports. We are always called
* under RTNL so can skip using rcu primitives.
*/
void br_manage_promisc(struct net_bridge *br)
{
struct net_bridge_port *p;
bool set_all = false;
/* If vlan filtering is disabled or bridge interface is placed
* into promiscuous mode, place all ports in promiscuous mode.
*/
if ((br->dev->flags & IFF_PROMISC) || !br_vlan_enabled(br->dev))
set_all = true;
list_for_each_entry(p, &br->port_list, list) {
if (set_all) {
br_port_set_promisc(p);
} else {
/* If the number of auto-ports is <= 1, then all other
* ports will have their output configuration
* statically specified through fdbs. Since ingress
* on the auto-port becomes forwarding/egress to other
* ports and egress configuration is statically known,
* we can say that ingress configuration of the
* auto-port is also statically known.
* This lets us disable promiscuous mode and write
* this config to hw.
*/
if (br->auto_cnt == 0 ||
(br->auto_cnt == 1 && br_auto_port(p)))
br_port_clear_promisc(p);
else
br_port_set_promisc(p);
}
}
}
int nbp_backup_change(struct net_bridge_port *p,
struct net_device *backup_dev)
{
struct net_bridge_port *old_backup = rtnl_dereference(p->backup_port);
struct net_bridge_port *backup_p = NULL;
ASSERT_RTNL();
if (backup_dev) {
if (!netif_is_bridge_port(backup_dev))
return -ENOENT;
backup_p = br_port_get_rtnl(backup_dev);
if (backup_p->br != p->br)
return -EINVAL;
}
if (p == backup_p)
return -EINVAL;
if (old_backup == backup_p)
return 0;
/* if the backup link is already set, clear it */
if (old_backup)
old_backup->backup_redirected_cnt--;
if (backup_p)
backup_p->backup_redirected_cnt++;
rcu_assign_pointer(p->backup_port, backup_p);
return 0;
}
static void nbp_backup_clear(struct net_bridge_port *p)
{
nbp_backup_change(p, NULL);
if (p->backup_redirected_cnt) {
struct net_bridge_port *cur_p;
list_for_each_entry(cur_p, &p->br->port_list, list) {
struct net_bridge_port *backup_p;
backup_p = rtnl_dereference(cur_p->backup_port);
if (backup_p == p)
nbp_backup_change(cur_p, NULL);
}
}
WARN_ON(rcu_access_pointer(p->backup_port) || p->backup_redirected_cnt);
}
static void nbp_update_port_count(struct net_bridge *br)
{
struct net_bridge_port *p;
u32 cnt = 0;
list_for_each_entry(p, &br->port_list, list) {
if (br_auto_port(p))
cnt++;
}
if (br->auto_cnt != cnt) {
br->auto_cnt = cnt;
br_manage_promisc(br);
}
}
static void nbp_delete_promisc(struct net_bridge_port *p)
{
/* If port is currently promiscuous, unset promiscuity.
* Otherwise, it is a static port so remove all addresses
* from it.
*/
dev_set_allmulti(p->dev, -1);
if (br_promisc_port(p))
dev_set_promiscuity(p->dev, -1);
else
br_fdb_unsync_static(p->br, p);
}
//static void release_nbp(struct kobject *kobj)
//{
// struct net_bridge_port *p
// = container_of(kobj, struct net_bridge_port, kobj);
// kfree(p);
//}
//static void brport_get_ownership(struct kobject *kobj, kuid_t *uid, kgid_t *gid)
//{
// struct net_bridge_port *p = kobj_to_brport(kobj);
// net_ns_get_ownership(dev_net(p->dev), uid, gid);
//}
//static struct kobj_type brport_ktype = {
//#ifdef CONFIG_SYSFS
// .sysfs_ops = &brport_sysfs_ops,
//#endif
// .release = release_nbp,
// .get_ownership = brport_get_ownership,
//};
static void destroy_nbp(struct net_bridge_port *p)
{
struct net_device *dev = p->dev;
p->br = NULL;
p->dev = NULL;
dev_put(dev);
// kobject_put(&p->kobj);
}
static void destroy_nbp_rcu(struct rcu_head *head)
{
struct net_bridge_port *p =
container_of(head, struct net_bridge_port, rcu);
destroy_nbp(p);
}
static unsigned get_max_headroom(struct net_bridge *br)
{
unsigned max_headroom = 0;
struct net_bridge_port *p;
list_for_each_entry(p, &br->port_list, list) {
unsigned dev_headroom = netdev_get_fwd_headroom(p->dev);
if (dev_headroom > max_headroom)
max_headroom = dev_headroom;
}
return max_headroom;
}
static void update_headroom(struct net_bridge *br, int new_hr)
{
struct net_bridge_port *p;
list_for_each_entry(p, &br->port_list, list)
netdev_set_rx_headroom(p->dev, new_hr);
br->dev->needed_headroom = new_hr;
}
/* Delete port(interface) from bridge is done in two steps.
* via RCU. First step, marks device as down. That deletes
* all the timers and stops new packets from flowing through.
*
* Final cleanup doesn't occur until after all CPU's finished
* processing packets.
*
* Protected from multiple admin operations by RTNL mutex
*/
static void del_nbp(struct net_bridge_port *p)
{
struct net_bridge *br = p->br;
struct net_device *dev = p->dev;
struct net_dhcp_info_opt *opt;
// sysfs_remove_link(br->ifobj, p->dev->name);
--br->port_count;
if (br->fast_forward) {
unregister_fast_path(&bridge_fast_forward, dev);
}
unregister_fast_path(&bridge_fast_path, dev);
nbp_delete_promisc(p);
spin_lock_bh(&br->lock);
// br_stp_disable_port(p);
{
p->state = BR_STATE_DISABLED;
p->vlan_def_state = BR_STATE_DISABLED;
br_multicast_disable_port(p);
}
spin_unlock_bh(&br->lock);
br_ifinfo_notify(RTM_DELLINK, NULL, p);
list_del_rcu(&p->list);
if (netdev_get_fwd_headroom(dev) == br->dev->needed_headroom)
update_headroom(br, get_max_headroom(br));
netdev_reset_rx_headroom(dev);
nbp_vlan_flush(p);
br_fdb_delete_by_port(br, p, 0, 1);
switchdev_deferred_process();
nbp_backup_clear(p);
nbp_update_port_count(br);
netdev_upper_dev_unlink(dev, br->dev);
dev->priv_flags &= ~IFF_BRIDGE_PORT;
rcu_assign_pointer(dev->master_dev, NULL);
netdev_rx_handler_unregister(dev);
br_multicast_del_port(p);
// kobject_uevent(&p->kobj, KOBJ_REMOVE);
// kobject_del(&p->kobj);
// br_netpoll_disable(p);
opt = rcu_dereference(p->info_option);
if (opt) {
call_rcu(&opt->rcu, destroy_info_opt);
rcu_assign_pointer(p->info_option, NULL);
}
call_rcu(&p->rcu, destroy_nbp_rcu);
br_update_fast_forward(br);
}
/* Delete bridge device */
void br_dev_delete(struct net_device *dev, struct list_head *head)
{
struct net_bridge *br = netdev_priv(dev);
struct net_bridge_port *p, *n;
list_for_each_entry_safe(p, n, &br->port_list, list) {
del_nbp(p);
}
// br_recalculate_neigh_suppress_enabled(br);
br_fdb_delete_by_port(br, NULL, 0, 1);
cancel_delayed_work_sync(&br->gc_work);
// br_sysfs_delbr(br->dev);
unregister_netdevice_queue(br->dev, head);
}
/* find an available port number */
static int find_portno(struct net_bridge *br)
{
int index;
struct net_bridge_port *p;
unsigned long *inuse;
inuse = bitmap_zalloc(BR_MAX_PORTS, GFP_KERNEL);
if (!inuse)
return -ENOMEM;
set_bit(0, inuse); /* zero is reserved */
list_for_each_entry(p, &br->port_list, list) {
set_bit(p->port_no, inuse);
}
index = find_first_zero_bit(inuse, BR_MAX_PORTS);
bitmap_free(inuse);
return (index >= BR_MAX_PORTS) ? -EXFULL : index;
}
/* called with RTNL but without bridge lock */
static struct net_bridge_port *new_nbp(struct net_bridge *br,
struct net_device *dev)
{
struct net_bridge_port *p;
int index, err;
index = find_portno(br);
if (index < 0)
return ERR_PTR(index);
p = kzalloc(sizeof(*p), GFP_KERNEL);
if (p == NULL)
return ERR_PTR(-ENOMEM);
p->br = br;
dev_hold(dev);
p->dev = dev;
// p->path_cost = port_cost(dev);
// p->priority = 0x8000 >> BR_PORT_BITS;
p->port_no = index;
p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD;
// br_init_port(p);
// br_set_state(p, BR_STATE_DISABLED);
// br_stp_port_timer_init(p);
p->state = BR_STATE_DISABLED;
p->vlan_def_state = BR_STATE_DISABLED;
p->horizon = BR_NOHORIZON;
p->switch_group = BR_NOSWITCH;
err = br_multicast_add_port(p);
if (err) {
dev_put(dev);
kfree(p);
p = ERR_PTR(err);
}
return p;
}
int br_add_bridge(struct net *net, const char *name)
{
struct net_device *dev;
int res;
dev = alloc_netdev(sizeof(struct net_bridge), name, NET_NAME_UNKNOWN,
br_dev_setup);
if (!dev)
return -ENOMEM;
dev_net_set(dev, net);
dev->rtnl_link_ops = &br_link_ops;
res = register_netdev(dev);
if (res)
free_netdev(dev);
return res;
}
int br_del_bridge(struct net *net, const char *name)
{
struct net_device *dev;
int ret = 0;
rtnl_lock();
dev = __dev_get_by_name(net, name);
if (dev == NULL)
ret = -ENXIO; /* Could not find device */
else if (!(dev->priv_flags & IFF_EBRIDGE)) {
/* Attempt to delete non bridge device! */
ret = -EPERM;
}
else if (dev->flags & IFF_UP) {
/* Not shutdown yet. */
ret = -EBUSY;
}
else
br_dev_delete(dev, NULL);
rtnl_unlock();
return ret;
}
#if 0
/* MTU of the bridge pseudo-device: ETH_DATA_LEN or the minimum of the ports */
static int br_mtu_min(const struct net_bridge *br)
{
const struct net_bridge_port *p;
int ret_mtu = 0;
list_for_each_entry(p, &br->port_list, list) {
int dm = p->dev->l2mtu;
if (!dm) dm = p->dev->mtu;
if (!ret_mtu || ret_mtu > dm)
ret_mtu = dm;
}
return ret_mtu ? ret_mtu : ETH_DATA_LEN;
}
void br_mtu_auto_adjust(struct net_bridge *br)
{
ASSERT_RTNL();
/* if the bridge MTU was manually configured don't mess with it */
if (br_opt_get(br, BROPT_MTU_SET_BY_USER))
return;
/* change to the minimum MTU and clear the flag which was set by
* the bridge ndo_change_mtu callback
*/
dev_set_mtu(br->dev, br_mtu_min(br));
br_opt_toggle(br, BROPT_MTU_SET_BY_USER, false);
}
#endif
static void br_set_gso_limits(struct net_bridge *br)
{
unsigned int gso_max_size = GSO_MAX_SIZE;
u16 gso_max_segs = GSO_MAX_SEGS;
const struct net_bridge_port *p;
list_for_each_entry(p, &br->port_list, list) {
gso_max_size = min(gso_max_size, p->dev->gso_max_size);
gso_max_segs = min(gso_max_segs, p->dev->gso_max_segs);
}
br->dev->gso_max_size = gso_max_size;
br->dev->gso_max_segs = gso_max_segs;
}
/*
* Recomputes features using slave's features
*/
netdev_features_t br_features_recompute(struct net_bridge *br,
netdev_features_t features)
{
struct net_bridge_port *p;
netdev_features_t mask;
if (list_empty(&br->port_list))
return features;
mask = features;
features &= ~NETIF_F_ONE_FOR_ALL;
list_for_each_entry(p, &br->port_list, list) {
features = netdev_increment_features(features,
p->dev->features, mask);
}
features = netdev_add_tso_features(features, mask);
return features;
}
/* called with RTNL */
int br_add_if(struct net_bridge *br, struct net_device *dev,
struct netlink_ext_ack *extack)
{
struct net_bridge_port *p;
int err = 0;
unsigned br_hr, dev_hr;
// bool changed_addr;
/* Don't allow bridging non-ethernet like devices, or DSA-enabled
* master network devices since the bridge layer rx_handler prevents
* the DSA fake ethertype handler to be invoked, so we do not strip off
* the DSA switch tag protocol header and the bridge layer just return
* RX_HANDLER_CONSUMED, stopping RX processing for these frames.
*/
if ((dev->flags & IFF_LOOPBACK) ||
(dev->type != ARPHRD_ETHER && dev->type != ARPHRD_PPP) ||
netdev_uses_dsa(dev))
return -EINVAL;
/* No bridging of bridges */
if (dev->netdev_ops->ndo_start_xmit == br_dev_xmit) {
NL_SET_ERR_MSG(extack,
"Can not enslave a bridge to a bridge");
return -ELOOP;
}
/* Device has master upper dev */
if (netdev_master_upper_dev_get(dev))
return -EBUSY;
/* No bridging devices that dislike that (e.g. wireless) */
if (dev->priv_flags & IFF_DONT_BRIDGE) {
NL_SET_ERR_MSG(extack,
"Device does not allow enslaving to a bridge");
return -EOPNOTSUPP;
}
p = new_nbp(br, dev);
if (IS_ERR(p))
return PTR_ERR(p);
call_netdevice_notifiers(NETDEV_JOIN, dev);
err = dev_set_allmulti(dev, 1);
if (err) {
kfree(p); /* kobject not yet init'd, manually free */
goto err1;
}
// err = kobject_init_and_add(&p->kobj, &brport_ktype, &(dev->dev.kobj),
// SYSFS_BRIDGE_PORT_ATTR);
// if (err)
// goto err2;
// err = br_sysfs_addif(p);
// if (err)
// goto err2;
// err = br_netpoll_enable(p);
// if (err)
// goto err3;
err = netdev_rx_handler_register(dev, br_handle_frame, p);
if (err)
goto err4;
dev->priv_flags |= IFF_BRIDGE_PORT;
err = netdev_master_upper_dev_link(dev, br->dev, NULL, NULL, extack);
if (err)
goto err5;
err = nbp_switchdev_mark_set(p);
if (err)
goto err6;
dev_disable_lro(dev);
list_add_rcu(&p->list, &br->port_list);
nbp_update_port_count(br);
netdev_update_features(br->dev);
br_hr = br->dev->needed_headroom;
dev_hr = netdev_get_fwd_headroom(dev);
if (br_hr < dev_hr)
update_headroom(br, dev_hr);
else
netdev_set_rx_headroom(dev, br_hr);
if (dev->type == ARPHRD_ETHER && br_fdb_insert(br, p, dev->dev_addr, 0))
netdev_err(dev, "failed insert local address bridge forwarding table\n");
if (br->dev->addr_assign_type != NET_ADDR_SET) {
/* Ask for permission to use this MAC address now, even if we
* don't end up choosing it below.
*/
err = dev_pre_changeaddr_notify(br->dev, dev->dev_addr, extack);
if (err)
goto err7;
}
err = nbp_vlan_init(p, extack);
if (err) {
netdev_err(dev, "failed to initialize vlan filtering on this port\n");
goto err7;
}
// spin_lock_bh(&br->lock);
// changed_addr = br_stp_recalculate_bridge_id(br);
// if (netif_running(dev) && netif_oper_up(dev) &&
// (br->dev->flags & IFF_UP))
// br_stp_enable_port(p);
// spin_unlock_bh(&br->lock);
br_ifinfo_notify(RTM_NEWLINK, NULL, p);
// if (changed_addr)
// call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
#if 0 // do not change bridge MTU under the hood
br_mtu_auto_adjust(br);
#endif
br_set_gso_limits(br);
// kobject_uevent(&p->kobj, KOBJ_ADD);
rcu_assign_pointer(dev->master_dev, br->dev);
++br->port_count;
register_fast_path(&bridge_fast_path, dev);
br_update_fast_forward(br);
return 0;
err7:
list_del_rcu(&p->list);
br_fdb_delete_by_port(br, p, 0, 1);
nbp_update_port_count(br);
err6:
netdev_upper_dev_unlink(dev, br->dev);
err5:
dev->priv_flags &= ~IFF_BRIDGE_PORT;
netdev_rx_handler_unregister(dev);
err4:
// br_netpoll_disable(p);
//err3:
// sysfs_remove_link(br->ifobj, p->dev->name);
//err2:
// kobject_put(&p->kobj);
dev_set_allmulti(dev, -1);
err1:
dev_put(dev);
return err;
}
/* called with RTNL */
int br_del_if(struct net_bridge *br, struct net_device *dev)
{
struct net_bridge_port *p;
// bool changed_addr;
p = br_port_get_rtnl(dev);
if (!p || p->br != br)
return -EINVAL;
/* Since more than one interface can be attached to a bridge,
* there still maybe an alternate path for netconsole to use;
* therefore there is no reason for a NETDEV_RELEASE event.
*/
del_nbp(p);
#if 0 // do not change bridge MTU under the hood
br_mtu_auto_adjust(br);
#endif
br_set_gso_limits(br);
// spin_lock_bh(&br->lock);
// changed_addr = br_stp_recalculate_bridge_id(br);
// spin_unlock_bh(&br->lock);
// if (changed_addr)
// call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev);
netdev_update_features(br->dev);
return 0;
}
void br_port_flags_change(struct net_bridge_port *p, unsigned long mask)
{
struct net_bridge *br = p->br;
if (mask & BR_AUTO_MASK)
nbp_update_port_count(br);
// if (mask & BR_NEIGH_SUPPRESS)
// br_recalculate_neigh_suppress_enabled(br);
}
bool br_port_flag_is_set(const struct net_device *dev, unsigned long flag)
{
struct net_bridge_port *p;
p = br_port_get_rtnl_rcu(dev);
if (!p)
return false;
return p->flags & flag;
}
EXPORT_SYMBOL_GPL(br_port_flag_is_set);
void br_update_fast_forward(struct net_bridge *br) {
struct net_bridge_port *p;
bool ff = br->allow_fast_forward && br->port_count == 2
&& !(br->dev->flags & IFF_PROMISC) && !br_opt_get(br, BROPT_VLAN_ENABLED)
&& !br->dhcp_snooping;
if (ff) {
struct net_bridge_port *p1 = list_entry(br->port_list.next, struct net_bridge_port, list);
struct net_bridge_port *p2 = list_entry(br->port_list.prev, struct net_bridge_port, list);
if (!ether_addr_equal(p1->dev->dev_addr, br->dev->dev_addr)
&& !ether_addr_equal(p2->dev->dev_addr, br->dev->dev_addr)) {
ff = false;
}
else if (p1->state != BR_STATE_FORWARDING || p2->state != BR_STATE_FORWARDING) {
ff = false;
}
else if (p1->switch_group != BR_NOSWITCH
|| p2->switch_group != BR_NOSWITCH) {
ff = false;
}
else if (!(p1->flags & BR_FLOOD) || !(p2->flags & BR_FLOOD)) {
ff = false;
}
else if (p1->horizon != BR_NOHORIZON
|| p2->horizon != BR_NOHORIZON) {
ff = false;
}
}
if (ff == br->fast_forward) {
return;
}
if (ff) {
struct net_bridge_port *p1 = list_entry(br->port_list.next, struct net_bridge_port, list);
struct net_bridge_port *p2 = list_entry(br->port_list.prev, struct net_bridge_port, list);
p1->dev->fp.forward_dev = p2->dev;
p2->dev->fp.forward_dev = p1->dev;
register_fast_path(&bridge_fast_forward, p1->dev);
register_fast_path(&bridge_fast_forward, p2->dev);
}
else {
list_for_each_entry(p, &br->port_list, list) {
unregister_fast_path(&bridge_fast_forward, p->dev);
}
}
br->fast_forward = ff;
}

View file

@ -0,0 +1,390 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Handle incoming frames
* Linux ethernet bridge
*
* Authors:
* Lennert Buytenhek <buytenh@gnu.org>
*/
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/netfilter_bridge.h>
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
#include <net/netfilter/nf_queue.h>
#endif
#include <linux/neighbour.h>
#include <net/arp.h>
#include <linux/export.h>
#include <linux/rculist.h>
#include "br_private.h"
#include "br_private_tunnel.h"
#include <linux/packet_hook.h>
DECLARE_PER_CPU(struct net_device *, per_cpu_port);
static int
br_netif_receive_skb(struct net *net, struct sock *sk, struct sk_buff *skb)
{
br_drop_fake_rtable(skb);
return netif_receive_skb(skb);
}
static int br_pass_frame_up(struct sk_buff *skb)
{
struct net_device *indev, *brdev = BR_INPUT_SKB_CB(skb)->brdev;
struct net_bridge *br = netdev_priv(brdev);
struct net_bridge_vlan_group *vg;
vg = br_vlan_group_rcu(br);
/* Bridge is just like any other port. Make sure the
* packet is allowed except in promisc modue when someone
* may be running packet capture.
*/
if (!(brdev->flags & IFF_PROMISC) &&
!br_allowed_egress(vg, skb)) {
kfree_skb(skb);
return NET_RX_DROP;
}
indev = skb->dev;
skb->dev = brdev;
skb = br_handle_vlan(br, NULL, vg, skb);
if (!skb)
return NET_RX_DROP;
/* update the multicast stats if the packet is IGMP/MLD */
br_multicast_count(br, NULL, skb, br_multicast_igmp_type(skb),
BR_MCAST_DIR_TX);
fast_path_sp_rx_inc(br->dev, skb->len);
if (likely(!get_cpu_var(per_cpu_port))) {
get_cpu_var(per_cpu_port) = indev;
}
int ret = NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN,
dev_net(indev), NULL, skb, indev, NULL,
br_netif_receive_skb);
get_cpu_var(per_cpu_port) = NULL;
return ret;
}
/* note: already called with rcu_read_lock (preempt_disabled) */
int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct net_bridge_port *p = br_port_get_rcu(skb->dev);
enum br_pkt_type pkt_type = BR_PKT_UNICAST;
struct net_bridge_fdb_entry *dst = NULL;
struct net_bridge_mdb_entry *mdst;
bool local_rcv, mcast_hit = false;
struct net_bridge *br;
u16 vid = 0;
u8 state;
if (!p || p->state == BR_STATE_DISABLED)
goto drop;
BR_INPUT_SKB_CB(skb)->src_port_hw_switched = p->switch_group != BR_NOSWITCH;
state = p->vlan_def_state;
if (!br_allowed_ingress(p->br, nbp_vlan_group_rcu(p), p->vlan_ingress, skb, &vid,
&state))
goto out;
nbp_switchdev_frame_mark(p, skb);
/* insert into forwarding database after filtering to avoid spoofing */
br = p->br;
if (p->flags & BR_LEARNING)
br_fdb_update(br, p, eth_hdr(skb)->h_source, vid, 0);
local_rcv = !!(br->dev->flags & IFF_PROMISC);
if (is_multicast_ether_addr(eth_hdr(skb)->h_dest)) {
/* by definition the broadcast is also a multicast address */
if (is_broadcast_ether_addr(eth_hdr(skb)->h_dest)) {
pkt_type = BR_PKT_BROADCAST;
local_rcv = true;
} else {
pkt_type = BR_PKT_MULTICAST;
if (br_multicast_rcv(br, p, skb, vid, false))
goto drop;
}
}
if (br_dhcp_rcv(br, p, skb, vid))
goto drop;
if (state == BR_STATE_LEARNING)
goto drop;
BR_INPUT_SKB_CB(skb)->brdev = br->dev;
BR_INPUT_SKB_CB(skb)->src_port_isolated = !!(p->flags & BR_ISOLATED);
BR_INPUT_SKB_CB(skb)->src_port_peer_link = !!(p->flags & BR_MLAG_PEER_LINK);
// if (IS_ENABLED(CONFIG_INET) &&
// (skb->protocol == htons(ETH_P_ARP) ||
// skb->protocol == htons(ETH_P_RARP))) {
// br_do_proxy_suppress_arp(skb, br, vid, p);
// } else if (IS_ENABLED(CONFIG_IPV6) &&
// skb->protocol == htons(ETH_P_IPV6) &&
// br_opt_get(br, BROPT_NEIGH_SUPPRESS_ENABLED) &&
// pskb_may_pull(skb, sizeof(struct ipv6hdr) +
// sizeof(struct nd_msg)) &&
// ipv6_hdr(skb)->nexthdr == IPPROTO_ICMPV6) {
// struct nd_msg *msg, _msg;
// msg = br_is_nd_neigh_msg(skb, &_msg);
// if (msg)
// br_do_suppress_nd(skb, br, vid, p, msg);
// }
switch (pkt_type) {
case BR_PKT_MULTICAST:
mdst = br_mdb_get(br, skb, vid);
if ((mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) &&
br_multicast_querier_exists(br, eth_hdr(skb))) {
if ((mdst && mdst->host_joined) ||
br_multicast_is_router(br)) {
local_rcv = true;
br->dev->stats.multicast++;
}
mcast_hit = true;
} else {
local_rcv = true;
br->dev->stats.multicast++;
}
break;
case BR_PKT_UNICAST:
dst = br_fdb_find_rcu(br, eth_hdr(skb)->h_dest, vid);
default:
break;
}
if (dst) {
unsigned long now = jiffies;
if (test_bit(BR_FDB_LOCAL, &dst->flags))
return br_pass_frame_up(skb);
if (now != dst->used)
dst->used = now;
br_forward(dst->dst, skb, local_rcv, false, p->horizon, p->switch_group);
} else {
if (!mcast_hit)
br_flood(br, skb, pkt_type, local_rcv, false, p->horizon, p->switch_group);
else
br_multicast_flood(mdst, skb, local_rcv, false, p->horizon, p->switch_group);
}
if (local_rcv)
return br_pass_frame_up(skb);
out:
return 0;
drop:
kfree_skb(skb);
goto out;
}
EXPORT_SYMBOL_GPL(br_handle_frame_finish);
static void __br_handle_local_finish(struct sk_buff *skb)
{
struct net_bridge_port *p = br_port_get_rcu(skb->dev);
u16 vid = 0;
/* check if vlan is allowed, to avoid spoofing */
if ((p->flags & BR_LEARNING) &&
nbp_state_should_learn(p) &&
!br_opt_get(p->br, BROPT_NO_LL_LEARN) &&
br_should_learn(p, skb, &vid))
br_fdb_update(p->br, p, eth_hdr(skb)->h_source, vid, 0);
}
/* note: already called with rcu_read_lock */
static int br_handle_local_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
{
__br_handle_local_finish(skb);
/* return 1 to signal the okfn() was called so it's ok to use the skb */
return 1;
}
static int nf_hook_bridge_pre(struct sk_buff *skb, struct sk_buff **pskb)
{
#ifdef CONFIG_NETFILTER_FAMILY_BRIDGE
struct nf_hook_entries *e = NULL;
struct nf_hook_state state;
unsigned int verdict, i;
struct net *net;
int ret;
net = dev_net(skb->dev);
#ifdef CONFIG_JUMP_LABEL
if (!static_key_false(&nf_hooks_needed[NFPROTO_BRIDGE][NF_BR_PRE_ROUTING]))
goto frame_finish;
#endif
e = rcu_dereference(net->nf.hooks_bridge[NF_BR_PRE_ROUTING]);
if (!e)
goto frame_finish;
nf_hook_state_init(&state, NF_BR_PRE_ROUTING,
NFPROTO_BRIDGE, skb->dev, NULL, NULL,
net, br_handle_frame_finish);
for (i = 0; i < e->num_hook_entries; i++) {
verdict = nf_hook_entry_hookfn(&e->hooks[i], skb, &state);
switch (verdict & NF_VERDICT_MASK) {
case NF_ACCEPT:
if (BR_INPUT_SKB_CB(skb)->br_netfilter_broute) {
*pskb = skb;
return RX_HANDLER_PASS;
}
break;
case NF_DROP:
kfree_skb(skb);
return RX_HANDLER_CONSUMED;
case NF_QUEUE:
ret = nf_queue(skb, &state, i, verdict);
if (ret == 1)
continue;
return RX_HANDLER_CONSUMED;
default: /* STOLEN */
return RX_HANDLER_CONSUMED;
}
}
frame_finish:
net = dev_net(skb->dev);
br_handle_frame_finish(net, NULL, skb);
#else
br_handle_frame_finish(dev_net(skb->dev), NULL, skb);
#endif
return RX_HANDLER_CONSUMED;
}
/*
* Return NULL if skb is handled
* note: already called with rcu_read_lock
*/
rx_handler_result_t br_handle_frame(struct sk_buff **pskb)
{
struct net_bridge_port *p;
struct sk_buff *skb = *pskb;
const unsigned char *dest = eth_hdr(skb)->h_dest;
if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
return RX_HANDLER_PASS;
/* do not bridge regular ip packets without ethernet header */
if (skb_mac_header(skb) == skb->data)
return RX_HANDLER_PASS;
if (!is_valid_ether_addr(eth_hdr(skb)->h_source))
goto drop;
skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb)
return RX_HANDLER_CONSUMED;
memset(skb->cb, 0, sizeof(struct br_input_skb_cb));
p = br_port_get_rcu(skb->dev);
if (p->flags & BR_VLAN_TUNNEL) {
if (br_handle_ingress_vlan_tunnel(skb, p,
nbp_vlan_group_rcu(p)))
goto drop;
}
if (unlikely(is_link_local_ether_addr(dest))) {
u16 fwd_mask = p->br->group_fwd_mask_required;
/*
* See IEEE 802.1D Table 7-10 Reserved addresses
*
* Assignment Value
* Bridge Group Address 01-80-C2-00-00-00
* (MAC Control) 802.3 01-80-C2-00-00-01
* (Link Aggregation) 802.3 01-80-C2-00-00-02
* 802.1X PAE address 01-80-C2-00-00-03
*
* 802.1AB LLDP 01-80-C2-00-00-0E
*
* Others reserved for future standardization
*/
switch (dest[5]) {
case 0x00: /* Bridge Group Address */
case 0x08:
/* If STP is turned off,
then must forward to keep loop detection */
if (p->br->stp_enabled == BR_NO_STP ||
fwd_mask & (1u << dest[5]))
goto forward;
*pskb = skb;
__br_handle_local_finish(skb);
return RX_HANDLER_PASS;
case 0x01: /* IEEE MAC (Pause) */
goto drop;
case 0x03:
case 0x0E:
if (p->br->stp_enabled == BR_NO_STP)
goto forward;
*pskb = skb;
__br_handle_local_finish(skb);
return RX_HANDLER_PASS;
default:
/* Allow selective forwarding for most other protocols */
fwd_mask |= p->br->group_fwd_mask;
if (fwd_mask & (1u << dest[5]))
goto forward;
}
/* The else clause should be hit when nf_hook():
* - returns < 0 (drop/error)
* - returns = 0 (stolen/nf_queue)
* Thus return 1 from the okfn() to signal the skb is ok to pass
*/
if (NF_HOOK(NFPROTO_BRIDGE, NF_BR_LOCAL_IN,
dev_net(skb->dev), NULL, skb, skb->dev, NULL,
br_handle_local_finish) == 1) {
return RX_HANDLER_PASS;
} else {
return RX_HANDLER_CONSUMED;
}
}
forward:
switch (p->state) {
case BR_STATE_FORWARDING:
case BR_STATE_LEARNING:
if (ether_addr_equal(p->br->dev->dev_addr, dest))
skb->pkt_type = PACKET_HOST;
return nf_hook_bridge_pre(skb, pskb);
default:
{
// XXX: original goal for this for detnet to be able to receive dhcp server
// offer/ack response and arp packets early to classify port as wan before any lan
// packets begin leaking into actual wan port
u16 vid = 0;
u8 state = BR_STATE_FORWARDING;
BR_INPUT_SKB_CB(skb)->src_port_hw_switched = p->switch_group != BR_NOSWITCH;
if (!br_allowed_ingress(p->br, nbp_vlan_group_rcu(p), p->vlan_ingress, skb, &vid,
&state))
goto out;
if (eth_hdr(skb)->h_proto == __constant_htons(ETH_P_IP)) {
custom_packet_hook_rx(skb);
}
else if (eth_hdr(skb)->h_proto == __constant_htons(ETH_P_ARP)) {
packet_hook_send(PACKET_HOOK_ARP, skb->dev->ifindex, skb);
}
}
drop:
kfree_skb(skb);
}
out:
return RX_HANDLER_CONSUMED;
}

View file

@ -0,0 +1,712 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Ioctl handler
* Linux ethernet bridge
*
* Authors:
* Lennert Buytenhek <buytenh@gnu.org>
*/
#include <linux/capability.h>
#include <linux/kernel.h>
#include <linux/if_bridge.h>
#include <linux/netdevice.h>
#include <linux/slab.h>
#include <linux/times.h>
#include <net/net_namespace.h>
#include <linux/uaccess.h>
#include "br_private.h"
/* called with RTNL */
static int get_bridge_ifindices(struct net *net, int *indices, int num)
{
struct net_device *dev;
int i = 0;
for_each_netdev(net, dev) {
if (i >= num)
break;
if (dev->priv_flags & IFF_EBRIDGE)
indices[i++] = dev->ifindex;
}
return i;
}
/* called with RTNL */
static void get_port_ifindices(struct net_bridge *br, int *ifindices, int num)
{
struct net_bridge_port *p;
int i = 0;
list_for_each_entry(p, &br->port_list, list) {
ifindices[i++] = p->dev->ifindex;
}
}
/*
* Format up to a page worth of forwarding table entries
* userbuf -- where to copy result
* maxnum -- maximum number of entries desired
* (limited to a page for sanity)
* offset -- number of records to skip
*/
static int get_fdb_entries(struct net_bridge *br, void __user *userbuf,
unsigned long maxnum, unsigned long offset)
{
int num;
void *buf;
size_t size;
/* Clamp size to PAGE_SIZE, test maxnum to avoid overflow */
if (maxnum > PAGE_SIZE/sizeof(struct abrctl_fdb_entry))
maxnum = PAGE_SIZE/sizeof(struct abrctl_fdb_entry);
size = maxnum * sizeof(struct abrctl_fdb_entry);
buf = kmalloc(size, GFP_USER);
if (!buf)
return -ENOMEM;
num = br_fdb_fillbuf(br, buf, maxnum, offset);
if (num > 0) {
if (copy_to_user(userbuf, buf, num*sizeof(struct abrctl_fdb_entry)))
num = -EFAULT;
}
kfree(buf);
return num;
}
static int add_del_if(struct net_bridge *br, int ifindex, int isadd)
{
struct net *net = dev_net(br->dev);
struct net_device *dev;
int ret;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
dev = __dev_get_by_index(net, ifindex);
if (dev == NULL)
return -EINVAL;
if (isadd)
ret = br_add_if(br, dev, NULL);
else
ret = br_del_if(br, dev);
return ret;
}
static struct net_bridge_port *find_port(struct net_bridge *br, int ifindex)
{
struct net_bridge_port *p = NULL;
list_for_each_entry(p, &br->port_list, list) {
if (p->dev->ifindex == ifindex) {
return p;
}
}
return NULL;
}
/*
* Legacy ioctl's through SIOCDEVPRIVATE
* This interface is deprecated because it was too difficult to
* to do the translation for 32/64bit ioctl compatability.
*/
static int old_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
struct net_bridge *br = netdev_priv(dev);
#ifdef CONFIG_64BIT
// XXX: 32bit compatibility for tile. 64bit userspace not supported
unsigned args_small[4];
unsigned long args[4];
if (copy_from_user(args_small, rq->ifr_data, sizeof(args_small)))
return -EFAULT;
args[0] = args_small[0];
args[1] = args_small[1];
args[2] = args_small[2];
args[3] = args_small[3];
switch (args[0]) {
case BRCTL_GET_BRIDGE_INFO:
case BRCTL_GET_PORT_LIST:
case BRCTL_GET_FDB_ENTRIES:
case ABRCTL_SET_MC_OPTS:
args[1] = (unsigned long)compat_ptr(args_small[1]);
break;
case ABRCTL_SET_INFO_OPT:
args[1] = (unsigned long)compat_ptr(args_small[1]);
args[2] = (unsigned long)compat_ptr(args_small[2]);
break;
}
#else
unsigned long args[4];
if (copy_from_user(args, rq->ifr_data, sizeof(args)))
return -EFAULT;
#endif
switch (args[0]) {
case BRCTL_ADD_IF:
case BRCTL_DEL_IF:
return add_del_if(br, args[1], args[0] == BRCTL_ADD_IF);
case BRCTL_GET_BRIDGE_INFO:
{
struct __bridge_info b;
memset(&b, 0, sizeof(struct __bridge_info));
rcu_read_lock();
/*
memcpy(&b.designated_root, &br->designated_root, 8);
memcpy(&b.bridge_id, &br->bridge_id, 8);
b.root_path_cost = br->root_path_cost;
b.max_age = jiffies_to_clock_t(br->max_age);
b.hello_time = jiffies_to_clock_t(br->hello_time);
b.forward_delay = br->forward_delay;
b.bridge_max_age = br->bridge_max_age;
b.bridge_hello_time = br->bridge_hello_time;
b.bridge_forward_delay = jiffies_to_clock_t(br->bridge_forward_delay);
b.topology_change = br->topology_change;
b.topology_change_detected = br->topology_change_detected;
b.root_port = br->root_port;
b.stp_enabled = br->stp_enabled;
*/
b.ageing_time = jiffies_to_clock_t(br->ageing_time);
/*
b.hello_timer_value = br_timer_value(&br->hello_timer);
b.tcn_timer_value = br_timer_value(&br->tcn_timer);
b.topology_change_timer_value = br_timer_value(&br->topology_change_timer);
*/
rcu_read_unlock();
if (copy_to_user((void __user *)args[1], &b, sizeof(b)))
return -EFAULT;
return 0;
}
case BRCTL_GET_PORT_LIST:
{
int num, *indices;
num = args[2];
if (num < 0)
return -EINVAL;
if (num == 0)
num = 256;
if (num > BR_MAX_PORTS)
num = BR_MAX_PORTS;
indices = kcalloc(num, sizeof(int), GFP_KERNEL);
if (indices == NULL)
return -ENOMEM;
get_port_ifindices(br, indices, num);
if (copy_to_user((void __user *)args[1], indices, num*sizeof(int)))
num = -EFAULT;
kfree(indices);
return num;
}
case BRCTL_SET_AGEING_TIME:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
br->ageing_time = args[1];
return 0;
case BRCTL_SET_BRIDGE_STP_STATE:
if (!capable(CAP_NET_ADMIN))
return -EPERM;
br->stp_enabled = args[1] & 3 ? BR_USER_STP : BR_NO_STP;
if (br->stp_enabled == BR_NO_STP) {
br->group_fwd_mask = 0xffff;
}
br->allow_fast_forward = !!(args[1] & 4);
br_multicast_toggle(br, !!(args[1] & 8));
br_vlan_filter_toggle(br, !!(args[1] & 0x10));
br_vlan_set_proto(br, args[2]);
br->dhcp_snooping = !!(args[1] & 0x20);
br->add_info_option = !!(args[1] & 0x40);
return 0;
case BRCTL_GET_FDB_ENTRIES:
return get_fdb_entries(br, (void __user *)args[1],
args[2], args[3]);
case ABRCTL_GET_MC_ROUTER: {
struct net_bridge_port *p;
int ret = 0;
spin_lock_bh(&br->multicast_lock);
if (args[1] == 0) {
ret = br_multicast_is_router(br);
}
else {
hlist_for_each_entry(p, &br->router_list, rlist) {
if (p->dev->ifindex == args[1]) {
ret = 1;
break;
}
}
}
spin_unlock_bh(&br->multicast_lock);
return ret;
}
case ABRCTL_FDB_FLUSH: {
struct net_bridge_port *p = NULL;
unsigned portidx = args[1];
unsigned vid = args[2];
// unsigned external = args[3];
int ret = 0;
spin_lock_bh(&br->lock);
if (portidx != 0) {
p = find_port(br, portidx);
if (!p) {
ret = -ENODEV;
goto out_delete;
}
}
// if (external) {
// br_fdb_flush_ext(br);
// } else {
br_fdb_delete_by_port(br, p, vid, 0);
// }
out_delete:
spin_unlock_bh(&br->lock);
return ret;
}
case ABRCTL_SET_PORTHORIZON: {
int ret = -ENODEV;
struct net_bridge_port *p;
spin_lock_bh(&br->lock);
list_for_each_entry(p, &br->port_list, list) {
if (p->dev->ifindex == args[1]) {
br_debug(br, "update %s horizon from 0x%x to 0x%x\n",
p->dev->name, p->horizon, (unsigned)args[2]);
p->horizon = args[2];
ret = 0;
break;
}
}
spin_unlock_bh(&br->lock);
br_update_fast_forward(br);
return ret;
}
case ABRCTL_SET_SWITCH_GROUP: {
int ret = -ENODEV;
struct net_bridge_port *p;
unsigned old = BR_NOSWITCH;
spin_lock_bh(&br->lock);
list_for_each_entry(p, &br->port_list, list) {
if (p->dev->ifindex == args[1]) {
old = p->switch_group;
p->switch_group = args[2];
ret = 0;
br_debug(p->br, "set group %s %u\n",
p->dev->name, p->switch_group);
break;
}
}
spin_unlock_bh(&br->lock);
if (ret)
return ret;
if (old != p->switch_group) {
int type;
if (p->switch_group == BR_NOSWITCH) {
br_multicast_offload(p, false);
type = RTM_DELNEIGH;
}
else {
br_multicast_offload(p, true);
type = RTM_NEWNEIGH;
}
/* If port removed from hw then notify to remove all user entries. Otherwise
* install them. */
struct net_bridge_fdb_entry *f;
rcu_read_lock();
hlist_for_each_entry_rcu(f, &br->fdb_list, fdb_node) {
if (test_bit(BR_FDB_ADDED_BY_USER, &f->flags) && f->dst == p) {
br_switchdev_fdb_notify(f, type);
}
}
rcu_read_unlock();
}
br_update_fast_forward(br);
return 0;
}
case ABRCTL_ADD_VLAN: {
unsigned ret = -ENODEV;
unsigned portidx = args[1];
unsigned vid = args[2];
unsigned flags = args[3];
struct net_bridge_port *p = NULL;
if (vid >= VLAN_N_VID)
return -EINVAL;
if (!br_opt_get(br, BROPT_VLAN_ENABLED))
return -EFAULT;
br_debug(br, "add vlan %d on %d flags %08x\n",
vid, portidx, flags);
bool changed;
if (portidx) {
list_for_each_entry(p, &br->port_list, list) {
if (p->dev->ifindex == portidx) {
ret = nbp_vlan_add(p, vid, flags, &changed, NULL);
break;
}
}
} else {
flags |= BRIDGE_VLAN_INFO_BRENTRY;
ret = br_vlan_add(br, vid, flags, &changed, NULL);
}
return ret;
}
case ABRCTL_DEL_VLAN: {
unsigned ret = -ENODEV;
unsigned portidx = args[1];
unsigned vid = args[2];
struct net_bridge_port *p = NULL;
if (vid >= VLAN_N_VID)
return -EINVAL;
if (!br_opt_get(br, BROPT_VLAN_ENABLED))
return -EFAULT;
br_debug(br, "del vlan %d on %d\n", vid, portidx);
if (portidx) {
list_for_each_entry(p, &br->port_list, list) {
if (p->dev->ifindex == portidx) {
ret = nbp_vlan_delete(p, vid);
break;
}
}
} else {
ret = br_vlan_delete(br, vid);
}
return ret;
}
case ABRCTL_SET_VLAN_OPTS: {
unsigned ret = -ENODEV;
unsigned portidx = args[1];
unsigned vlan_ingress = args[2];
struct net_bridge_port *p = NULL;
if (!br_opt_get(br, BROPT_VLAN_ENABLED))
return -EFAULT;
spin_lock_bh(&br->lock);
br_debug(br, "set vlan opts on %u opts %u %lu %lu\n",
portidx,
vlan_ingress & BR_IN_FRAME_TYPES,
vlan_ingress & BR_IN_FILTERING,
vlan_ingress & BR_IN_TAG_STACKING);
if (portidx) {
list_for_each_entry(p, &br->port_list, list) {
if (p->dev->ifindex == portidx) {
p->vlan_ingress = vlan_ingress;
ret = 0;
break;
}
}
} else {
br->vlan_ingress = vlan_ingress;
}
spin_unlock_bh(&br->lock);
return ret;
}
case ABRCTL_SET_PORT_OPTS: {
unsigned ret = -ENODEV;
unsigned portidx = args[1];
unsigned flags = args[2];
struct net_bridge_port *p = NULL;
spin_lock_bh(&br->lock);
list_for_each_entry(p, &br->port_list, list) {
if (p->dev->ifindex == portidx) {
br_debug(br, "set port %s flag %08x\n",
p->dev->name, flags);
p->flags = flags;
ret = 0;
break;
}
}
spin_unlock_bh(&br->lock);
br_update_fast_forward(br);
return ret;
}
case ABRCTL_SET_INFO_OPT: {
unsigned ret = 0;
unsigned portidx = args[1];
struct bridge_info_opt req;
struct net_bridge_port *p = NULL;
struct net_dhcp_info_opt *old_info, *new_info;
if (copy_from_user(&req, (void __user *)args[2], sizeof(req)))
return -EFAULT;
spin_lock_bh(&br->lock);
p = find_port(br, portidx);
if (!p) {
ret = -ENODEV;
goto out_info_opt1;
}
new_info = kzalloc(sizeof(*new_info), GFP_KERNEL);
if (!new_info) {
ret = -ENOMEM;
goto out_info_opt1;
}
new_info->circuit_id = kzalloc(req.circuit_id_size, GFP_KERNEL);
if (!new_info->circuit_id) {
ret = -ENOMEM;
goto out_info_opt2;
}
new_info->remote_id = kzalloc(req.remote_id_size, GFP_KERNEL);
if (!new_info->remote_id) {
ret = -ENOMEM;
goto out_info_opt3;
}
if (copy_from_user(new_info->circuit_id,
compat_ptr(req.circuit_id),
req.circuit_id_size)) {
ret = -EFAULT;
goto out_info_opt4;
}
if (copy_from_user(new_info->remote_id,
compat_ptr(req.remote_id),
req.remote_id_size)) {
ret = -EFAULT;
goto out_info_opt4;
}
old_info = rcu_dereference(p->info_option);
rcu_assign_pointer(p->info_option, new_info);
spin_unlock_bh(&br->lock);
if (old_info) {
call_rcu(&old_info->rcu, destroy_info_opt);
}
return 0;
out_info_opt4:
kfree(new_info->remote_id);
out_info_opt3:
kfree(new_info->circuit_id);
out_info_opt2:
kfree(new_info);
out_info_opt1:
spin_unlock_bh(&br->lock);
return ret;
}
case ABRCTL_SET_MC_ROUTER: {
unsigned ret = -ENODEV;
unsigned portidx = args[1];
unsigned val = args[2];
struct net_bridge_port *p = NULL;
spin_lock_bh(&br->lock);
if (portidx) {
list_for_each_entry(p, &br->port_list, list) {
if (p->dev->ifindex == portidx) {
local_bh_disable();
br_multicast_set_port_router(p, val);
local_bh_enable();
ret = 0;
break;
}
}
} else {
br_multicast_set_router(br, val);
ret = 0;
}
spin_unlock_bh(&br->lock);
return ret;
}
case ABRCTL_SET_MC_OPTS: {
struct abrctl_mc_opts x;
if (copy_from_user(&x, (void __user *)args[1], sizeof(x)) != 0)
return -EFAULT;
spin_lock_bh(&br->multicast_lock);
br->multicast_last_member_count = x.last_member_count;
br->multicast_startup_query_count = x.startup_query_count;
br->multicast_last_member_interval = x.last_member_interval;
br->multicast_membership_interval = x.membership_interval;
br->multicast_querier_interval = x.querier_interval;
br->multicast_query_interval = x.query_interval;
br->multicast_query_response_interval = x.query_response_interval;
br->multicast_startup_query_interval = x.startup_query_interval;
br->multicast_igmp_version = x.igmp_version;
br->multicast_mld_version = x.mld_version;
spin_unlock_bh(&br->multicast_lock);
br_multicast_set_querier(br, x.querier);
return 0;
}
case ABRCTL_GET_FAST_FORWARD: {
return br->fast_forward;
}
case ABRCTL_GET_MC_QUERIER: {
struct abrctl_mc_querier queriers[2] = {};
struct net_bridge_port *port;
spin_lock_bh(&br->multicast_lock);
port = rcu_dereference(br->ip4_querier.port);
queriers[0].portidx = port ? port->dev->ifindex : 0;
queriers[0].addr.u.ip4 = br->ip4_querier.addr.u.ip4;
port = rcu_dereference(br->ip6_querier.port);
queriers[1].portidx = port ? port->dev->ifindex : 0;
queriers[1].addr.u.ip6 = br->ip6_querier.addr.u.ip6;
if (copy_to_user((void __user *)args[1], &queriers, sizeof(queriers)))
return -EFAULT;
spin_unlock_bh(&br->multicast_lock);
return 0;
}
default:
break;
}
return -EOPNOTSUPP;
}
static int old_deviceless(struct net *net, void __user *uarg)
{
unsigned long args[3];
if (copy_from_user(args, uarg, sizeof(args)))
return -EFAULT;
switch (args[0]) {
case BRCTL_GET_VERSION:
return BRCTL_VERSION;
case BRCTL_GET_BRIDGES:
{
int *indices;
int ret = 0;
if (args[2] >= 2048)
return -ENOMEM;
indices = kcalloc(args[2], sizeof(int), GFP_KERNEL);
if (indices == NULL)
return -ENOMEM;
args[2] = get_bridge_ifindices(net, indices, args[2]);
ret = copy_to_user((void __user *)args[1], indices, args[2]*sizeof(int))
? -EFAULT : args[2];
kfree(indices);
return ret;
}
case BRCTL_ADD_BRIDGE:
case BRCTL_DEL_BRIDGE:
{
char buf[IFNAMSIZ];
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(buf, (void __user *)args[1], IFNAMSIZ))
return -EFAULT;
buf[IFNAMSIZ-1] = 0;
if (args[0] == BRCTL_ADD_BRIDGE)
return br_add_bridge(net, buf);
return br_del_bridge(net, buf);
}
}
return -EOPNOTSUPP;
}
int br_ioctl_deviceless_stub(struct net *net, unsigned int cmd, void __user *uarg)
{
switch (cmd) {
case SIOCGIFBR:
case SIOCSIFBR:
return old_deviceless(net, uarg);
case SIOCBRADDBR:
case SIOCBRDELBR:
{
char buf[IFNAMSIZ];
if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(buf, uarg, IFNAMSIZ))
return -EFAULT;
buf[IFNAMSIZ-1] = 0;
if (cmd == SIOCBRADDBR)
return br_add_bridge(net, buf);
return br_del_bridge(net, buf);
}
}
return -EOPNOTSUPP;
}
int br_dev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
struct net_bridge *br = netdev_priv(dev);
switch (cmd) {
case SIOCDEVPRIVATE:
return old_dev_ioctl(dev, rq, cmd);
case SIOCBRADDIF:
case SIOCBRDELIF:
return add_del_if(br, rq->ifr_ifindex, cmd == SIOCBRADDIF);
}
br_debug(br, "Bridge does not support ioctl 0x%x\n", cmd);
return -EOPNOTSUPP;
}

1019
2025-03-19/bridge2/br_mdb.c Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,241 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Handle firewalling
* Linux ethernet bridge
*
* Authors:
* Lennert Buytenhek <buytenh@gnu.org>
* Bart De Schuymer <bdschuym@pandora.be>
*
* Lennert dedicates this file to Kerstin Wurdinger.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/ip.h>
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
#include <linux/if_ether.h>
#include <linux/if_vlan.h>
#include <linux/if_pppox.h>
#include <linux/ppp_defs.h>
#include <linux/netfilter_bridge.h>
#include <linux/netfilter_ipv4.h>
#include <linux/netfilter_ipv6.h>
#include <linux/netfilter_arp.h>
#include <linux/in_route.h>
#include <linux/inetdevice.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/addrconf.h>
#include <net/route.h>
#include <net/netfilter/br_netfilter.h>
#include <linux/uaccess.h>
#include "br_private.h"
#ifdef CONFIG_SYSCTL
#include <linux/sysctl.h>
#endif
/* We only check the length. A bridge shouldn't do any hop-by-hop stuff
* anyway
*/
static int br_nf_check_hbh_len(struct sk_buff *skb)
{
unsigned char *raw = (u8 *)(ipv6_hdr(skb) + 1);
u32 pkt_len;
const unsigned char *nh = skb_network_header(skb);
int off = raw - nh;
int len = (raw[1] + 1) << 3;
if ((raw + len) - skb->data > skb_headlen(skb))
goto bad;
off += 2;
len -= 2;
while (len > 0) {
int optlen = nh[off + 1] + 2;
switch (nh[off]) {
case IPV6_TLV_PAD1:
optlen = 1;
break;
case IPV6_TLV_PADN:
break;
case IPV6_TLV_JUMBO:
if (nh[off + 1] != 4 || (off & 3) != 2)
goto bad;
pkt_len = ntohl(*(__be32 *)(nh + off + 2));
if (pkt_len <= IPV6_MAXPLEN ||
ipv6_hdr(skb)->payload_len)
goto bad;
if (pkt_len > skb->len - sizeof(struct ipv6hdr))
goto bad;
if (pskb_trim_rcsum(skb,
pkt_len + sizeof(struct ipv6hdr)))
goto bad;
nh = skb_network_header(skb);
break;
default:
if (optlen > len)
goto bad;
break;
}
off += optlen;
len -= optlen;
}
if (len == 0)
return 0;
bad:
return -1;
}
int br_validate_ipv6(struct net *net, struct sk_buff *skb)
{
const struct ipv6hdr *hdr;
struct inet6_dev *idev = __in6_dev_get(skb->dev);
u32 pkt_len;
u8 ip6h_len = sizeof(struct ipv6hdr);
if (!pskb_may_pull(skb, ip6h_len))
goto inhdr_error;
if (skb->len < ip6h_len)
goto drop;
hdr = ipv6_hdr(skb);
if (hdr->version != 6)
goto inhdr_error;
pkt_len = ntohs(hdr->payload_len);
if (pkt_len || hdr->nexthdr != NEXTHDR_HOP) {
if (pkt_len + ip6h_len > skb->len) {
__IP6_INC_STATS(net, idev,
IPSTATS_MIB_INTRUNCATEDPKTS);
goto drop;
}
if (pskb_trim_rcsum(skb, pkt_len + ip6h_len)) {
__IP6_INC_STATS(net, idev,
IPSTATS_MIB_INDISCARDS);
goto drop;
}
hdr = ipv6_hdr(skb);
}
if (hdr->nexthdr == NEXTHDR_HOP && br_nf_check_hbh_len(skb))
goto drop;
memset(IP6CB(skb), 0, sizeof(struct inet6_skb_parm));
/* No IP options in IPv6 header; however it should be
* checked if some next headers need special treatment
*/
return 0;
inhdr_error:
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
drop:
return -1;
}
static inline bool
br_nf_ipv6_daddr_was_changed(const struct sk_buff *skb,
const struct nf_bridge_info *nf_bridge)
{
return memcmp(&nf_bridge->ipv6_daddr, &ipv6_hdr(skb)->daddr,
sizeof(ipv6_hdr(skb)->daddr)) != 0;
}
/* PF_BRIDGE/PRE_ROUTING: Undo the changes made for ip6tables
* PREROUTING and continue the bridge PRE_ROUTING hook. See comment
* for br_nf_pre_routing_finish(), same logic is used here but
* equivalent IPv6 function ip6_route_input() called indirectly.
*/
static int br_nf_pre_routing_finish_ipv6(struct net *net, struct sock *sk, struct sk_buff *skb)
{
struct nf_bridge_info *nf_bridge = nf_bridge_info_get(skb);
struct rtable *rt;
struct net_device *dev = skb->dev;
const struct nf_ipv6_ops *v6ops = nf_get_ipv6_ops();
nf_bridge->frag_max_size = IP6CB(skb)->frag_max_size;
if (nf_bridge->pkt_otherhost) {
skb->pkt_type = PACKET_OTHERHOST;
nf_bridge->pkt_otherhost = false;
}
nf_bridge->in_prerouting = 0;
if (br_nf_ipv6_daddr_was_changed(skb, nf_bridge)) {
skb_dst_drop(skb);
v6ops->route_input(skb);
if (skb_dst(skb)->error) {
kfree_skb(skb);
return 0;
}
if (skb_dst(skb)->dev == dev) {
skb->dev = nf_bridge->physindev;
nf_bridge_update_protocol(skb);
nf_bridge_push_encap_header(skb);
br_nf_hook_thresh(NF_BR_PRE_ROUTING,
net, sk, skb, skb->dev, NULL,
br_nf_pre_routing_finish_bridge);
return 0;
}
ether_addr_copy(eth_hdr(skb)->h_dest, dev->dev_addr);
skb->pkt_type = PACKET_HOST;
} else {
rt = bridge_parent_rtable(nf_bridge->physindev);
if (!rt) {
kfree_skb(skb);
return 0;
}
skb_dst_set_noref(skb, &rt->dst);
}
skb->dev = nf_bridge->physindev;
nf_bridge_update_protocol(skb);
nf_bridge_push_encap_header(skb);
br_nf_hook_thresh(NF_BR_PRE_ROUTING, net, sk, skb,
skb->dev, NULL, br_handle_frame_finish);
return 0;
}
/* Replicate the checks that IPv6 does on packet reception and pass the packet
* to ip6tables.
*/
unsigned int br_nf_pre_routing_ipv6(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state)
{
struct nf_bridge_info *nf_bridge;
if (br_validate_ipv6(state->net, skb))
return NF_DROP;
nf_bridge = nf_bridge_alloc(skb);
if (!nf_bridge)
return NF_DROP;
if (!setup_pre_routing(skb, state->net))
return NF_DROP;
nf_bridge = nf_bridge_info_get(skb);
nf_bridge->ipv6_daddr = ipv6_hdr(skb)->daddr;
skb->protocol = htons(ETH_P_IPV6);
skb->transport_header = skb->network_header + sizeof(struct ipv6hdr);
NF_HOOK(NFPROTO_IPV6, NF_INET_PRE_ROUTING, state->net, state->sk, skb,
skb->dev, NULL,
br_nf_pre_routing_finish_ipv6);
return NF_STOLEN;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,294 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Bridge per vlan tunnel port dst_metadata netlink control interface
*
* Authors:
* Roopa Prabhu <roopa@cumulusnetworks.com>
*/
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/etherdevice.h>
#include <net/rtnetlink.h>
#include <net/net_namespace.h>
#include <net/sock.h>
#include <uapi/linux/if_bridge.h>
#include <net/dst_metadata.h>
#include "br_private.h"
#include "br_private_tunnel.h"
static size_t __get_vlan_tinfo_size(void)
{
return nla_total_size(0) + /* nest IFLA_BRIDGE_VLAN_TUNNEL_INFO */
nla_total_size(sizeof(u32)) + /* IFLA_BRIDGE_VLAN_TUNNEL_ID */
nla_total_size(sizeof(u16)) + /* IFLA_BRIDGE_VLAN_TUNNEL_VID */
nla_total_size(sizeof(u16)); /* IFLA_BRIDGE_VLAN_TUNNEL_FLAGS */
}
static bool vlan_tunid_inrange(struct net_bridge_vlan *v_curr,
struct net_bridge_vlan *v_last)
{
__be32 tunid_curr = tunnel_id_to_key32(v_curr->tinfo.tunnel_id);
__be32 tunid_last = tunnel_id_to_key32(v_last->tinfo.tunnel_id);
return (be32_to_cpu(tunid_curr) - be32_to_cpu(tunid_last)) == 1;
}
static int __get_num_vlan_tunnel_infos(struct net_bridge_vlan_group *vg)
{
struct net_bridge_vlan *v, *vtbegin = NULL, *vtend = NULL;
int num_tinfos = 0;
/* Count number of vlan infos */
list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
/* only a context, bridge vlan not activated */
if (!br_vlan_should_use(v) || !v->tinfo.tunnel_id)
continue;
if (!vtbegin) {
goto initvars;
} else if ((v->vid - vtend->vid) == 1 &&
vlan_tunid_inrange(v, vtend)) {
vtend = v;
continue;
} else {
if ((vtend->vid - vtbegin->vid) > 0)
num_tinfos += 2;
else
num_tinfos += 1;
}
initvars:
vtbegin = v;
vtend = v;
}
if (vtbegin && vtend) {
if ((vtend->vid - vtbegin->vid) > 0)
num_tinfos += 2;
else
num_tinfos += 1;
}
return num_tinfos;
}
int br_get_vlan_tunnel_info_size(struct net_bridge_vlan_group *vg)
{
int num_tinfos;
if (!vg)
return 0;
rcu_read_lock();
num_tinfos = __get_num_vlan_tunnel_infos(vg);
rcu_read_unlock();
return num_tinfos * __get_vlan_tinfo_size();
}
static int br_fill_vlan_tinfo(struct sk_buff *skb, u16 vid,
__be64 tunnel_id, u16 flags)
{
__be32 tid = tunnel_id_to_key32(tunnel_id);
struct nlattr *tmap;
tmap = nla_nest_start_noflag(skb, IFLA_BRIDGE_VLAN_TUNNEL_INFO);
if (!tmap)
return -EMSGSIZE;
if (nla_put_u32(skb, IFLA_BRIDGE_VLAN_TUNNEL_ID,
be32_to_cpu(tid)))
goto nla_put_failure;
if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_VID,
vid))
goto nla_put_failure;
if (nla_put_u16(skb, IFLA_BRIDGE_VLAN_TUNNEL_FLAGS,
flags))
goto nla_put_failure;
nla_nest_end(skb, tmap);
return 0;
nla_put_failure:
nla_nest_cancel(skb, tmap);
return -EMSGSIZE;
}
static int br_fill_vlan_tinfo_range(struct sk_buff *skb,
struct net_bridge_vlan *vtbegin,
struct net_bridge_vlan *vtend)
{
int err;
if (vtend && (vtend->vid - vtbegin->vid) > 0) {
/* add range to skb */
err = br_fill_vlan_tinfo(skb, vtbegin->vid,
vtbegin->tinfo.tunnel_id,
BRIDGE_VLAN_INFO_RANGE_BEGIN);
if (err)
return err;
err = br_fill_vlan_tinfo(skb, vtend->vid,
vtend->tinfo.tunnel_id,
BRIDGE_VLAN_INFO_RANGE_END);
if (err)
return err;
} else {
err = br_fill_vlan_tinfo(skb, vtbegin->vid,
vtbegin->tinfo.tunnel_id,
0);
if (err)
return err;
}
return 0;
}
int br_fill_vlan_tunnel_info(struct sk_buff *skb,
struct net_bridge_vlan_group *vg)
{
struct net_bridge_vlan *vtbegin = NULL;
struct net_bridge_vlan *vtend = NULL;
struct net_bridge_vlan *v;
int err;
/* Count number of vlan infos */
list_for_each_entry_rcu(v, &vg->vlan_list, vlist) {
/* only a context, bridge vlan not activated */
if (!br_vlan_should_use(v))
continue;
if (!v->tinfo.tunnel_dst)
continue;
if (!vtbegin) {
goto initvars;
} else if ((v->vid - vtend->vid) == 1 &&
vlan_tunid_inrange(v, vtend)) {
vtend = v;
continue;
} else {
err = br_fill_vlan_tinfo_range(skb, vtbegin, vtend);
if (err)
return err;
}
initvars:
vtbegin = v;
vtend = v;
}
if (vtbegin) {
err = br_fill_vlan_tinfo_range(skb, vtbegin, vtend);
if (err)
return err;
}
return 0;
}
static const struct nla_policy vlan_tunnel_policy[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1] = {
[IFLA_BRIDGE_VLAN_TUNNEL_ID] = { .type = NLA_U32 },
[IFLA_BRIDGE_VLAN_TUNNEL_VID] = { .type = NLA_U16 },
[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS] = { .type = NLA_U16 },
};
static int br_vlan_tunnel_info(struct net_bridge_port *p, int cmd,
u16 vid, u32 tun_id, bool *changed)
{
int err = 0;
if (!p)
return -EINVAL;
switch (cmd) {
case RTM_SETLINK:
err = nbp_vlan_tunnel_info_add(p, vid, tun_id);
if (!err)
*changed = true;
break;
case RTM_DELLINK:
if (!nbp_vlan_tunnel_info_delete(p, vid))
*changed = true;
break;
}
return err;
}
int br_parse_vlan_tunnel_info(struct nlattr *attr,
struct vtunnel_info *tinfo)
{
struct nlattr *tb[IFLA_BRIDGE_VLAN_TUNNEL_MAX + 1];
u32 tun_id;
u16 vid, flags = 0;
int err;
memset(tinfo, 0, sizeof(*tinfo));
err = nla_parse_nested_deprecated(tb, IFLA_BRIDGE_VLAN_TUNNEL_MAX,
attr, vlan_tunnel_policy, NULL);
if (err < 0)
return err;
if (!tb[IFLA_BRIDGE_VLAN_TUNNEL_ID] ||
!tb[IFLA_BRIDGE_VLAN_TUNNEL_VID])
return -EINVAL;
tun_id = nla_get_u32(tb[IFLA_BRIDGE_VLAN_TUNNEL_ID]);
vid = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_VID]);
if (vid >= VLAN_VID_MASK)
return -ERANGE;
if (tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS])
flags = nla_get_u16(tb[IFLA_BRIDGE_VLAN_TUNNEL_FLAGS]);
tinfo->tunid = tun_id;
tinfo->vid = vid;
tinfo->flags = flags;
return 0;
}
int br_process_vlan_tunnel_info(struct net_bridge *br,
struct net_bridge_port *p, int cmd,
struct vtunnel_info *tinfo_curr,
struct vtunnel_info *tinfo_last,
bool *changed)
{
int err;
if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) {
if (tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN)
return -EINVAL;
memcpy(tinfo_last, tinfo_curr, sizeof(struct vtunnel_info));
} else if (tinfo_curr->flags & BRIDGE_VLAN_INFO_RANGE_END) {
int t, v;
if (!(tinfo_last->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN))
return -EINVAL;
if ((tinfo_curr->vid - tinfo_last->vid) !=
(tinfo_curr->tunid - tinfo_last->tunid))
return -EINVAL;
t = tinfo_last->tunid;
for (v = tinfo_last->vid; v <= tinfo_curr->vid; v++) {
err = br_vlan_tunnel_info(p, cmd, v, t, changed);
if (err)
return err;
t++;
}
memset(tinfo_last, 0, sizeof(struct vtunnel_info));
memset(tinfo_curr, 0, sizeof(struct vtunnel_info));
} else {
if (tinfo_last->flags)
return -EINVAL;
err = br_vlan_tunnel_info(p, cmd, tinfo_curr->vid,
tinfo_curr->tunid, changed);
if (err)
return err;
memset(tinfo_last, 0, sizeof(struct vtunnel_info));
memset(tinfo_curr, 0, sizeof(struct vtunnel_info));
}
return 0;
}

View file

@ -0,0 +1,91 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Handle firewalling core
* Linux ethernet bridge
*
* Authors:
* Lennert Buytenhek <buytenh@gnu.org>
* Bart De Schuymer <bdschuym@pandora.be>
*
* Lennert dedicates this file to Kerstin Wurdinger.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/in_route.h>
#include <linux/inetdevice.h>
#include <net/route.h>
#include "br_private.h"
#ifdef CONFIG_SYSCTL
#include <linux/sysctl.h>
#endif
static void fake_update_pmtu(struct dst_entry *dst, struct sock *sk,
struct sk_buff *skb, u32 mtu,
bool confirm_neigh)
{
}
static void fake_redirect(struct dst_entry *dst, struct sock *sk,
struct sk_buff *skb)
{
}
static u32 *fake_cow_metrics(struct dst_entry *dst, unsigned long old)
{
return NULL;
}
static struct neighbour *fake_neigh_lookup(const struct dst_entry *dst,
struct sk_buff *skb,
const void *daddr)
{
return NULL;
}
static unsigned int fake_mtu(const struct dst_entry *dst)
{
return dst->dev->mtu;
}
static struct dst_ops fake_dst_ops = {
.family = AF_INET,
.update_pmtu = fake_update_pmtu,
.redirect = fake_redirect,
.cow_metrics = fake_cow_metrics,
.neigh_lookup = fake_neigh_lookup,
.mtu = fake_mtu,
};
/*
* Initialize bogus route table used to keep netfilter happy.
* Currently, we fill in the PMTU entry because netfilter
* refragmentation needs it, and the rt_flags entry because
* ipt_REJECT needs it. Future netfilter modules might
* require us to fill additional fields.
*/
static const u32 br_dst_default_metrics[RTAX_MAX] = {
[RTAX_MTU - 1] = 1500,
};
void br_netfilter_rtable_init(struct net_bridge *br)
{
struct rtable *rt = &br->fake_rtable;
atomic_set(&rt->dst.__refcnt, 1);
rt->dst.dev = br->dev;
dst_init_metrics(&rt->dst, br_dst_default_metrics, true);
rt->dst.flags = DST_NOXFRM | DST_FAKE_RTABLE;
rt->dst.ops = &fake_dst_ops;
}
int __init br_nf_core_init(void)
{
return dst_entries_init(&fake_dst_ops);
}
void br_nf_core_fini(void)
{
dst_entries_destroy(&fake_dst_ops);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Bridge per vlan tunnels
*
* Authors:
* Roopa Prabhu <roopa@cumulusnetworks.com>
*/
#ifndef _BR_PRIVATE_TUNNEL_H
#define _BR_PRIVATE_TUNNEL_H
struct vtunnel_info {
u32 tunid;
u16 vid;
u16 flags;
};
/* br_netlink_tunnel.c */
int br_parse_vlan_tunnel_info(struct nlattr *attr,
struct vtunnel_info *tinfo);
int br_process_vlan_tunnel_info(struct net_bridge *br,
struct net_bridge_port *p,
int cmd,
struct vtunnel_info *tinfo_curr,
struct vtunnel_info *tinfo_last,
bool *changed);
int br_get_vlan_tunnel_info_size(struct net_bridge_vlan_group *vg);
int br_fill_vlan_tunnel_info(struct sk_buff *skb,
struct net_bridge_vlan_group *vg);
#ifdef CONFIG_BRIDGE_VLAN_FILTERING
/* br_vlan_tunnel.c */
int vlan_tunnel_init(struct net_bridge_vlan_group *vg);
void vlan_tunnel_deinit(struct net_bridge_vlan_group *vg);
int nbp_vlan_tunnel_info_delete(struct net_bridge_port *port, u16 vid);
int nbp_vlan_tunnel_info_add(struct net_bridge_port *port, u16 vid, u32 tun_id);
void nbp_vlan_tunnel_info_flush(struct net_bridge_port *port);
void vlan_tunnel_info_del(struct net_bridge_vlan_group *vg,
struct net_bridge_vlan *vlan);
int br_handle_ingress_vlan_tunnel(struct sk_buff *skb,
struct net_bridge_port *p,
struct net_bridge_vlan_group *vg);
int br_handle_egress_vlan_tunnel(struct sk_buff *skb,
struct net_bridge_vlan *vlan);
#endif
#endif

View file

@ -0,0 +1,172 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
#include <linux/skbuff.h>
#include <net/switchdev.h>
#include "br_private.h"
static int br_switchdev_mark_get(struct net_bridge *br, struct net_device *dev)
{
struct net_bridge_port *p;
/* dev is yet to be added to the port list. */
list_for_each_entry(p, &br->port_list, list) {
if (netdev_port_same_parent_id(dev, p->dev))
return p->offload_fwd_mark;
}
return ++br->offload_fwd_mark;
}
int nbp_switchdev_mark_set(struct net_bridge_port *p)
{
struct netdev_phys_item_id ppid = { };
int err;
ASSERT_RTNL();
err = dev_get_port_parent_id(p->dev, &ppid, true);
if (err) {
if (err == -EOPNOTSUPP)
return 0;
return err;
}
p->offload_fwd_mark = br_switchdev_mark_get(p->br, p->dev);
return 0;
}
void nbp_switchdev_frame_mark(const struct net_bridge_port *p,
struct sk_buff *skb)
{
if (skb->offload_fwd_mark && !WARN_ON_ONCE(!p->offload_fwd_mark))
BR_INPUT_SKB_CB(skb)->offload_fwd_mark = p->offload_fwd_mark;
}
bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
const struct sk_buff *skb)
{
return !skb->offload_fwd_mark ||
BR_INPUT_SKB_CB(skb)->offload_fwd_mark != p->offload_fwd_mark;
}
/* Flags that can be offloaded to hardware */
#define BR_PORT_FLAGS_HW_OFFLOAD (BR_LEARNING | BR_FLOOD | \
BR_MCAST_FLOOD | BR_BCAST_FLOOD)
int br_switchdev_set_port_flag(struct net_bridge_port *p,
unsigned long flags,
unsigned long mask)
{
struct switchdev_attr attr = {
.orig_dev = p->dev,
.id = SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS,
.u.brport_flags = mask,
};
struct switchdev_notifier_port_attr_info info = {
.attr = &attr,
};
int err;
if (mask & ~BR_PORT_FLAGS_HW_OFFLOAD)
return 0;
/* We run from atomic context here */
err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev,
&info.info, NULL);
err = notifier_to_errno(err);
if (err == -EOPNOTSUPP)
return 0;
if (err) {
br_warn(p->br, "bridge flag offload is not supported %u(%s)\n",
(unsigned int)p->port_no, p->dev->name);
return -EOPNOTSUPP;
}
attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS;
attr.flags = SWITCHDEV_F_DEFER;
attr.u.brport_flags = flags;
err = switchdev_port_attr_set(p->dev, &attr);
if (err) {
br_warn(p->br, "error setting offload flag on port %u(%s)\n",
(unsigned int)p->port_no, p->dev->name);
return err;
}
return 0;
}
static void
br_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac,
u16 vid, struct net_device *dev,
bool added_by_user, bool offloaded)
{
struct switchdev_notifier_fdb_info info;
unsigned long notifier_type;
info.addr = mac;
info.vid = vid;
info.added_by_user = added_by_user;
info.offloaded = offloaded;
notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE;
call_switchdev_notifiers(notifier_type, dev, &info.info, NULL);
}
void
br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type)
{
if (!fdb->dst)
return;
switch (type) {
case RTM_DELNEIGH:
br_switchdev_fdb_call_notifiers(false, fdb->key.addr.addr,
fdb->key.vlan_id,
fdb->dst->dev,
test_bit(BR_FDB_ADDED_BY_USER,
&fdb->flags),
test_bit(BR_FDB_OFFLOADED,
&fdb->flags));
break;
case RTM_NEWNEIGH:
br_switchdev_fdb_call_notifiers(true, fdb->key.addr.addr,
fdb->key.vlan_id,
fdb->dst->dev,
test_bit(BR_FDB_ADDED_BY_USER,
&fdb->flags),
test_bit(BR_FDB_OFFLOADED,
&fdb->flags));
break;
}
}
int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags,
struct netlink_ext_ack *extack)
{
struct switchdev_obj_port_vlan v = {
.obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.flags = flags,
.vid_begin = vid,
.vid_end = vid,
};
return switchdev_port_obj_add(dev, &v.obj, extack);
}
int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid)
{
struct switchdev_obj_port_vlan v = {
.obj.orig_dev = dev,
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.vid_begin = vid,
.vid_end = vid,
};
return switchdev_port_obj_del(dev, &v.obj);
}

2045
2025-03-19/bridge2/br_vlan.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,160 @@
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (c) 2020, Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
#include <linux/slab.h>
#include "br_private.h"
/* check if the options between two vlans are equal */
bool br_vlan_opts_eq(const struct net_bridge_vlan *v1,
const struct net_bridge_vlan *v2)
{
return v1->state == v2->state;
}
bool br_vlan_opts_fill(struct sk_buff *skb, const struct net_bridge_vlan *v)
{
return !nla_put_u8(skb, BRIDGE_VLANDB_ENTRY_STATE,
br_vlan_get_state(v));
}
size_t br_vlan_opts_nl_size(void)
{
return nla_total_size(sizeof(u8)); /* BRIDGE_VLANDB_ENTRY_STATE */
}
static int br_vlan_modify_state(struct net_bridge_vlan_group *vg,
struct net_bridge_vlan *v,
u8 state,
bool *changed,
struct netlink_ext_ack *extack)
{
struct net_bridge *br;
ASSERT_RTNL();
if (state > BR_STATE_BLOCKING) {
NL_SET_ERR_MSG_MOD(extack, "Invalid vlan state");
return -EINVAL;
}
if (br_vlan_is_brentry(v))
br = v->br;
else
br = v->port->br;
// if (br->stp_enabled == BR_KERNEL_STP) {
// NL_SET_ERR_MSG_MOD(extack, "Can't modify vlan state when using kernel STP");
// return -EBUSY;
// }
if (v->state == state)
return 0;
if (v->vid == br_get_pvid(vg))
br_vlan_set_pvid_state(vg, state);
br_vlan_set_state(v, state);
*changed = true;
return 0;
}
static int br_vlan_process_one_opts(const struct net_bridge *br,
const struct net_bridge_port *p,
struct net_bridge_vlan_group *vg,
struct net_bridge_vlan *v,
struct nlattr **tb,
bool *changed,
struct netlink_ext_ack *extack)
{
int err;
*changed = false;
if (tb[BRIDGE_VLANDB_ENTRY_STATE]) {
u8 state = nla_get_u8(tb[BRIDGE_VLANDB_ENTRY_STATE]);
err = br_vlan_modify_state(vg, v, state, changed, extack);
if (err)
return err;
}
return 0;
}
int br_vlan_process_options(const struct net_bridge *br,
const struct net_bridge_port *p,
struct net_bridge_vlan *range_start,
struct net_bridge_vlan *range_end,
struct nlattr **tb,
struct netlink_ext_ack *extack)
{
struct net_bridge_vlan *v, *curr_start = NULL, *curr_end = NULL;
struct net_bridge_vlan_group *vg;
int vid, err = 0;
u16 pvid;
if (p)
vg = nbp_vlan_group(p);
else
vg = br_vlan_group(br);
if (!range_start || !br_vlan_should_use(range_start)) {
NL_SET_ERR_MSG_MOD(extack, "Vlan range start doesn't exist, can't process options");
return -ENOENT;
}
if (!range_end || !br_vlan_should_use(range_end)) {
NL_SET_ERR_MSG_MOD(extack, "Vlan range end doesn't exist, can't process options");
return -ENOENT;
}
pvid = br_get_pvid(vg);
for (vid = range_start->vid; vid <= range_end->vid; vid++) {
bool changed = false;
v = br_vlan_find(vg, vid);
if (!v || !br_vlan_should_use(v)) {
NL_SET_ERR_MSG_MOD(extack, "Vlan in range doesn't exist, can't process options");
err = -ENOENT;
break;
}
err = br_vlan_process_one_opts(br, p, vg, v, tb, &changed,
extack);
if (err)
break;
if (changed) {
/* vlan options changed, check for range */
if (!curr_start) {
curr_start = v;
curr_end = v;
continue;
}
if (v->vid == pvid ||
!br_vlan_can_enter_range(v, curr_end)) {
br_vlan_notify(br, p, curr_start->vid,
curr_end->vid, RTM_NEWVLAN);
curr_start = v;
}
curr_end = v;
} else {
/* nothing changed and nothing to notify yet */
if (!curr_start)
continue;
br_vlan_notify(br, p, curr_start->vid, curr_end->vid,
RTM_NEWVLAN);
curr_start = NULL;
curr_end = NULL;
}
}
if (curr_start)
br_vlan_notify(br, p, curr_start->vid, curr_end->vid,
RTM_NEWVLAN);
return err;
}

View file

@ -0,0 +1,200 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Bridge per vlan tunnel port dst_metadata handling code
*
* Authors:
* Roopa Prabhu <roopa@cumulusnetworks.com>
*/
#include <linux/kernel.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
#include <linux/slab.h>
#include <net/switchdev.h>
#include <net/dst_metadata.h>
#include "br_private.h"
#include "br_private_tunnel.h"
static inline int br_vlan_tunid_cmp(struct rhashtable_compare_arg *arg,
const void *ptr)
{
const struct net_bridge_vlan *vle = ptr;
__be64 tunid = *(__be64 *)arg->key;
return vle->tinfo.tunnel_id != tunid;
}
static const struct rhashtable_params br_vlan_tunnel_rht_params = {
.head_offset = offsetof(struct net_bridge_vlan, tnode),
.key_offset = offsetof(struct net_bridge_vlan, tinfo.tunnel_id),
.key_len = sizeof(__be64),
.nelem_hint = 3,
.obj_cmpfn = br_vlan_tunid_cmp,
.automatic_shrinking = true,
};
static struct net_bridge_vlan *br_vlan_tunnel_lookup(struct rhashtable *tbl,
u64 tunnel_id)
{
return rhashtable_lookup_fast(tbl, &tunnel_id,
br_vlan_tunnel_rht_params);
}
void vlan_tunnel_info_del(struct net_bridge_vlan_group *vg,
struct net_bridge_vlan *vlan)
{
if (!vlan->tinfo.tunnel_dst)
return;
rhashtable_remove_fast(&vg->tunnel_hash, &vlan->tnode,
br_vlan_tunnel_rht_params);
vlan->tinfo.tunnel_id = 0;
dst_release(&vlan->tinfo.tunnel_dst->dst);
vlan->tinfo.tunnel_dst = NULL;
}
static int __vlan_tunnel_info_add(struct net_bridge_vlan_group *vg,
struct net_bridge_vlan *vlan, u32 tun_id)
{
struct metadata_dst *metadata = NULL;
__be64 key = key32_to_tunnel_id(cpu_to_be32(tun_id));
int err;
if (vlan->tinfo.tunnel_dst)
return -EEXIST;
metadata = __ip_tun_set_dst(0, 0, 0, 0, 0, TUNNEL_KEY,
key, 0);
if (!metadata)
return -EINVAL;
metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX | IP_TUNNEL_INFO_BRIDGE;
vlan->tinfo.tunnel_dst = metadata;
vlan->tinfo.tunnel_id = key;
err = rhashtable_lookup_insert_fast(&vg->tunnel_hash, &vlan->tnode,
br_vlan_tunnel_rht_params);
if (err)
goto out;
return 0;
out:
dst_release(&vlan->tinfo.tunnel_dst->dst);
vlan->tinfo.tunnel_dst = NULL;
vlan->tinfo.tunnel_id = 0;
return err;
}
/* Must be protected by RTNL.
* Must be called with vid in range from 1 to 4094 inclusive.
*/
int nbp_vlan_tunnel_info_add(struct net_bridge_port *port, u16 vid, u32 tun_id)
{
struct net_bridge_vlan_group *vg;
struct net_bridge_vlan *vlan;
ASSERT_RTNL();
vg = nbp_vlan_group(port);
vlan = br_vlan_find(vg, vid);
if (!vlan)
return -EINVAL;
return __vlan_tunnel_info_add(vg, vlan, tun_id);
}
/* Must be protected by RTNL.
* Must be called with vid in range from 1 to 4094 inclusive.
*/
int nbp_vlan_tunnel_info_delete(struct net_bridge_port *port, u16 vid)
{
struct net_bridge_vlan_group *vg;
struct net_bridge_vlan *v;
ASSERT_RTNL();
vg = nbp_vlan_group(port);
v = br_vlan_find(vg, vid);
if (!v)
return -ENOENT;
vlan_tunnel_info_del(vg, v);
return 0;
}
static void __vlan_tunnel_info_flush(struct net_bridge_vlan_group *vg)
{
struct net_bridge_vlan *vlan, *tmp;
list_for_each_entry_safe(vlan, tmp, &vg->vlan_list, vlist)
vlan_tunnel_info_del(vg, vlan);
}
void nbp_vlan_tunnel_info_flush(struct net_bridge_port *port)
{
struct net_bridge_vlan_group *vg;
ASSERT_RTNL();
vg = nbp_vlan_group(port);
__vlan_tunnel_info_flush(vg);
}
int vlan_tunnel_init(struct net_bridge_vlan_group *vg)
{
return rhashtable_init(&vg->tunnel_hash, &br_vlan_tunnel_rht_params);
}
void vlan_tunnel_deinit(struct net_bridge_vlan_group *vg)
{
rhashtable_destroy(&vg->tunnel_hash);
}
int br_handle_ingress_vlan_tunnel(struct sk_buff *skb,
struct net_bridge_port *p,
struct net_bridge_vlan_group *vg)
{
struct ip_tunnel_info *tinfo = skb_tunnel_info(skb);
struct net_bridge_vlan *vlan;
if (!vg || !tinfo)
return 0;
/* if already tagged, ignore */
if (skb_vlan_tagged(skb))
return 0;
/* lookup vid, given tunnel id */
vlan = br_vlan_tunnel_lookup(&vg->tunnel_hash, tinfo->key.tun_id);
if (!vlan)
return 0;
skb_dst_drop(skb);
__vlan_hwaccel_put_tag(skb, p->br->vlan_proto, vlan->vid);
return 0;
}
int br_handle_egress_vlan_tunnel(struct sk_buff *skb,
struct net_bridge_vlan *vlan)
{
int err;
if (!vlan || !vlan->tinfo.tunnel_id)
return 0;
if (unlikely(!skb_vlan_tag_present(skb)))
return 0;
skb_dst_drop(skb);
err = skb_vlan_pop(skb);
if (err)
return err;
skb_dst_set(skb, dst_clone(&vlan->tinfo.tunnel_dst->dst));
return 0;
}

144
2025-03-19/bridge2/ctl.h Normal file
View file

@ -0,0 +1,144 @@
#ifndef ADDITIONAL_BRCTL_H
#define ADDITIONAL_BRCTL_H
#include <linux/types.h>
#ifndef __KERNEL__
#define BIT(x) (1 << (x))
#endif
//#define BR_HAIRPIN_MODE BIT(0)
//#define BR_BPDU_GUARD BIT(1)
//#define BR_ROOT_BLOCK BIT(2)
#define BR_MULTICAST_FAST_LEAVE BIT(3)
//#define BR_ADMIN_COST BIT(4)
#define BR_LEARNING BIT(5)
#define BR_FLOOD BIT(6)
#define BR_AUTO_MASK (BR_FLOOD | BR_LEARNING)
//#define BR_PROMISC BIT(7)
//#define BR_PROXYARP BIT(8)
//#define BR_LEARNING_SYNC BIT(9)
//#define BR_PROXYARP_WIFI BIT(10)
#define BR_MCAST_FLOOD BIT(11)
//#define BR_MULTICAST_TO_UNICAST BIT(12)
//#define BR_VLAN_TUNNEL BIT(13)
#define BR_BCAST_FLOOD BIT(14)
#define BR_MLAG_PEER_LINK BIT(29)
#define BR_MLAG_DUAL_LINK BIT(30)
#define BR_TRUSTED_PORT BIT(31)
#define BR_DEFAULT (BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD)
#define BR_NOHORIZON 0
#define BR_NOSWITCH -1u
#define BRFDB_LOCAL 0
#define BRFDB_STATIC 10
#define BRFDB_LEARNED 20
#define BRFDB_ALL 0xffff
#define BR_IN_FRAME_TYPES_OFF 0
#define BR_IN_FRAME_TYPES 0x3
#define BR_PORT_FRAME_ADMIT_ALL 0
#define BR_PORT_FRAME_ADMIT_TAGGED 1
#define BR_PORT_FRAME_ADMIT_UNTAGGED 2
#define BR_IN_FILTERING_OFF 2
#define BR_IN_FILTERING BIT(BR_IN_FILTERING_OFF)
#define BR_IN_TAG_STACKING_OFF 3
#define BR_IN_TAG_STACKING BIT(BR_IN_TAG_STACKING_OFF)
#define ABRCTL_FDB_INSERT 100
#define ABRCTL_FDB_DELETE 101
#define ABRCTL_SET_STP_STATE 102 // port in arg0, mstid in arg1, state in arg2
#define ABRCTL_SET_PORTHORIZON 103 // port in arg0, horizon in arg1
#define ABRCTL_SET_SWITCH_GROUP 104 // port in arg0, switch_group in arg1
#define ABRCTL_GET_MDB 105
#define ABRCTL_GET_MC_ROUTER 106
#define ABRCTL_FDB_FLUSH 107 // port in arg0, vid in arg1, dynamic/static in arg1
#define ABRCTL_ADD_VLAN 108 // port in arg1, vid in arg2, flags in arg3
#define ABRCTL_DEL_VLAN 109 // port in arg1, vid in arg2
#define ABRCTL_SET_INSTANCE 110 // abrctl_vlan_range
#define ABRCTL_DEL_INSTANCE 111 // mstid in arg1
#define ABRCTL_SET_VLAN_OPTS 112 // port in arg1, frameType in arg2, ingFilter in arg3
#define ABRCTL_SET_PORT_OPTS 113 // port in arg1, flags in arg2
#define ABRCTL_SET_INFO_OPT 114 // port in arg1, bridge_info_opt in arg2
#define ABRCTL_SET_MC_ROUTER 115
#define ABRCTL_SET_MC_OPTS 116
#define ABRCTL_GET_FAST_FORWARD 117
#define ABRCTL_GET_MC_QUERIER 118
struct abrctl_fdb_insert {
unsigned char addr[6];
unsigned type;
unsigned portidx;
};
struct abrctl_fdb_entry {
__u8 mac_addr[6];
__u8 port_no;
__u8 is_local;
__u32 ageing_timer_value;
__u8 port_hi;
__u8 is_static;
__u16 vlan_id;
};
// include/uapi/linux/if_bridge.h
struct abrctl_mdb_entry {
__u32 ifindex;
#define MDB_TEMPORARY 0
#define MDB_PERMANENT 1
//__u8 state;
__u16 vid;
struct {
union {
__be32 ip4;
struct in6_addr ip6;
} u;
__be16 proto;
} addr;
};
struct abrctl_vlan_range {
unsigned arg;
unsigned range_count;
const unsigned *vlan_ranges;
};
struct bridge_vlan_range {
unsigned range_count;
__u16 *vlan_ranges;
};
struct bridge_info_opt {
unsigned circuit_id_size;
unsigned circuit_id;
unsigned remote_id_size;
unsigned remote_id;
};
struct abrctl_mc_opts {
unsigned last_member_count;
unsigned startup_query_count;
unsigned last_member_interval;
unsigned membership_interval;
unsigned querier_interval;
unsigned query_interval;
unsigned query_response_interval;
unsigned startup_query_interval;
unsigned char querier;
unsigned char igmp_version;
unsigned char mld_version;
};
struct abrctl_mc_querier {
struct {
union {
__be32 ip4;
struct in6_addr ip6;
} u;
} addr;
unsigned portidx;
};
#endif

View file

@ -0,0 +1,241 @@
#include <linux/module.h>
#include <linux/packet_hook.h>
#include <linux/counter.h>
#include <net/sch_generic.h>
#include "br_private.h"
static inline int check_hw(const struct net_bridge_port *p,
unsigned from_switch_group)
{
return p->switch_group == BR_NOSWITCH
|| p->switch_group != from_switch_group;
}
static inline int should_deliver(const struct net_bridge_port *p,
const struct net_device *src, unsigned horizon) {
return (src != p->dev
&& p->state == BR_STATE_FORWARDING
&& (horizon == BR_NOHORIZON || horizon != p->horizon));
}
static int br_dev_fast_path_xmit(struct net_device *dev, struct fp_buf *fpb) {
struct net_bridge *br = netdev_priv(dev);
struct net_bridge_fdb_entry *fdb;
struct net_device *dst;
if (is_multicast_ether_addr(fpb_data(fpb))) {
goto slow_path;
}
if (br_opt_get(br, BROPT_VLAN_ENABLED)) {
goto slow_path;
}
fdb = br_fdb_find_rcu(br, fpb_data(fpb), 0);
if (!fdb || !fdb->dst) {
goto slow_path;
}
if (fdb->dst->state != BR_STATE_FORWARDING) {
goto slow_path;
}
dst = fdb->dst->dev;
if (dst->l2mtu + ETH_HLEN < fpb_len(fpb)) {
goto slow_path;
}
fast_path_fp_tx_inc(dev, fpb);
return fast_path_tx(dst, fpb);
slow_path:
return fast_path_tx_slow(dev, fpb);
}
static struct net_device *fast_forward_handler(struct net_device *dev, struct fp_buf *fpb) {
unsigned char *data = fpb_data(fpb);
unsigned x;
struct net_device *dst = dev->fp.forward_dev;
unsigned a1 = get_unaligned((unsigned *)data);
unsigned short a2 = get_unaligned((unsigned short *)&data[4]);
if (unlikely(is_multicast_ether_addr(data))) {
return NULL;
}
x = *(unsigned *)dev->fp.dev_addr - a1;
x |= *(unsigned short *)&dev->fp.dev_addr[4] - a2;
if (unlikely(!x)) {
return NULL;
}
x = *(unsigned *)dst->fp.dev_addr - a1;
x |= *(unsigned short *)&dst->fp.dev_addr[4] - a2;
if (unlikely(!x)) {
return NULL;
}
if (unlikely(dst->l2mtu + ETH_HLEN < fpb_len(fpb))) {
return NULL;
}
{
struct fast_path_state *fps = &get_cpu_var(per_cpu_fp_state);
fps->forward_packets++;
fps->forward_bytes += fpb_len(fpb);
}
return dst;
}
static inline void fast_path_bridge_inc(struct fp_buf *fpb) {
struct fast_path_state *fps = &get_cpu_var(per_cpu_fp_state);
fps->bridge_packets++;
fps->bridge_bytes += fpb_len(fpb);
}
DECLARE_PER_CPU(struct net_device *, per_cpu_port);
static struct net_device *fast_path_handler(struct net_device *src,
struct fp_buf *fpb) {
struct ethhdr *eth = (struct ethhdr *)fpb_data(fpb);
struct net_device *dst;
struct net_bridge_port *src_port;
struct net_bridge_port *dst_port;
struct net_bridge_fdb_entry *fdb;
struct net_bridge *br;
// printk("bridge fast path rx %s %u\n", src->name, fpb_len(fpb));
// fpb_dump("in", fpb);
src_port = br_port_get_check_rcu(src);
if (!src_port || src_port->state != BR_STATE_FORWARDING) {
return NULL;
}
br = src_port->br;
if (br_opt_get(br, BROPT_VLAN_ENABLED) || br->dhcp_snooping) {
return NULL;
}
if (fpb_len(fpb) < ETH_HLEN) {
return NULL;
}
if (is_multicast_ether_addr(eth->h_dest)) {
return NULL;
}
// update src
rcu_read_lock();
fdb = br_fdb_find_rcu(br, eth->h_source, 0);
rcu_read_unlock();
if (!fdb) {
return NULL;
}
if (test_bit(BR_FDB_LOCAL, &fdb->flags)) {
return NULL;
}
if (fdb->dst != src_port) {
fdb->dst = src_port;
// Can skip since, fdb is dumped periodically
//fdb_notify(br, fdb, RTM_NEWNEIGH);
}
{
unsigned long now = jiffies;
if (fdb->updated != now) {
fdb->updated = now;
}
}
// printk("br rx %s %pM->%pM %pM\n", src->name, eth->h_source, eth->h_dest, br->dev->fp.dev_addr);
if (ether_addr_equal(eth->h_dest, br->dev->fp.dev_addr)) {
goto rx;
}
rcu_read_lock();
fdb = br_fdb_find_rcu(br, eth->h_dest, 0);
rcu_read_unlock();
if (!fdb) {
return NULL;
}
if (test_bit(BR_FDB_LOCAL, &fdb->flags)) {
rx:
if (likely(!*this_cpu_ptr(&per_cpu_port))) {
*this_cpu_ptr(&per_cpu_port) = src;
}
fast_path_rx_noinvalidate(br->dev, fpb);
*this_cpu_ptr(&per_cpu_port) = NULL;
fast_path_bridge_inc(fpb);
return FAST_PATH_CONSUMED;
}
if (!fdb->dst) {
return NULL;
}
dst_port = fdb->dst;
dst = dst_port->dev;
// final stuff
if (!should_deliver(dst_port, src, src_port->horizon)
|| !check_hw(dst_port, src_port->switch_group)) {
// printk("should not deliver\n");
return NULL;
}
if (dst->l2mtu + ETH_HLEN < fpb_len(fpb)) {
return NULL;
}
fast_path_bridge_inc(fpb);
// printk("ok %s\n", dst->name);
// fpb_dump("ou", fpb);
return dst;
}
struct fast_path bridge_fast_path = {
.handler = fast_path_handler,
.priority = FP_PRIO_BRIDGE,
};
struct fast_path bridge_fast_forward = {
.handler = fast_forward_handler,
.priority = FP_PRIO_FORWARD,
};
extern struct net_device_ops br_netdev_ops;
static ssize_t bridge_active_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf) {
return sprintf(buf, "%d\n", !bridge_fast_path.suspended);
}
static ssize_t bridge_counter_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf) {
return sprintf(buf, "%llu %llu %llu %llu\n",
fp_global.bridge_packets, fp_global.bridge_bytes,
fp_global.forward_packets, fp_global.forward_bytes);
}
static struct kobj_attribute bridge_active_attribute =
__ATTR(fp_bridge_active, 0664, bridge_active_show, NULL);
static struct kobj_attribute bridge_counter_attribute =
__ATTR(fp_bridge_counter, 0664, bridge_counter_show, NULL);
static struct attribute *attrs[] = {
&bridge_active_attribute.attr,
&bridge_counter_attribute.attr,
NULL,
};
static struct attribute_group attr_group = {
.attrs = attrs,
};
int __init bridge_fast_path_init(void) {
int ret = sysfs_create_group(kernel_kobj, &attr_group);
if (ret) {
return ret;
}
rcu_assign_pointer(br_netdev_ops.ndo_fast_path_xmit, br_dev_fast_path_xmit);
return 0;
}
void __exit bridge_fast_path_exit(void) {
sysfs_remove_group(kernel_kobj, &attr_group);
}

29
2025-03-19/btest/Makefile Normal file
View file

@ -0,0 +1,29 @@
MODULE = btest.ko
btest_FILES = btest.c
btest_PREFIX = misc
ifneq ($(KERNELRELEASE),)
EXTRA_CFLAGS := $(CFLAGS)
define moddef
obj-m += $(1).o
ifneq ($(1).o, $(2))
$(1)-y := $(2)
EXTRA_CFLAGS += $($(1)_CFLAGS)
endif
endef
$(foreach m, $(MODULE), $(eval $(call moddef,$(m:.ko=),$($(m:.ko=)_FILES:.c=.o))))
EXTRA_CFLAGS := $(subst -I, -I$(src)/, $(EXTRA_CFLAGS))
else
KERNELDIR := /lib/modules/$$(uname -r)/build
all::
$(MAKE) -C $(KERNELDIR) M=$$(pwd) $@
endif

629
2025-03-19/btest/btest.c Normal file
View file

@ -0,0 +1,629 @@
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/udp.h>
#include <linux/file.h>
#include <linux/random.h>
#include <linux/net.h>
#include <linux/workqueue.h>
#include <linux/miscdevice.h>
#include <net/sock.h>
#include <net/udp.h>
#include <net/ip.h>
#include <net/route.h>
#include <net/ip6_route.h>
#include <net/ip6_checksum.h>
#include <linux/netfilter_ipv4.h>
#include <linux/in_route.h>
#include <asm/unaligned.h>
#include <asm/div64.h>
#include "btest.h"
#define BTEST_MASK_LEN 4
#define BTEST_BIT_COUNT 32
MODULE_LICENSE("GPL");
#define STAT_WORK 0
#define STAT_TIMER 1
#define STAT_XMIT 2
#define STAT_XMIT_FAIL 3
#define STAT_MAX 4
//#define STATS
#include <linux/stats.h>
#ifdef STATS
static struct stats_descr stats_btest = {
.name = "btest",
.interval = HZ,
.elements = {
{
.name = "work",
},
{
.name = "timer",
// .no_print = true,
// .no_reset = true,
},
{
.name = "xmit",
},
{
.name = "xmit_fail",
},
}
};
#endif
static unsigned btest_cpu;
struct btest_state {
bool started;
struct btest_params params;
struct btest_info info;
unsigned cpu;
void (*old_data_ready)(struct sock *sk);
// lost packet stats accounting
unsigned first;
unsigned unable;
unsigned maxseq;
unsigned lastseq;
unsigned mask[BTEST_MASK_LEN];
// transmit
unsigned packet_nsecs;
unsigned seq;
u64 next_tx;
struct socket *socket;
struct sock *sk;
struct work_struct work;
struct hrtimer timer;
spinlock_t lock;
#ifdef STATS
struct stats_descr stats;
#endif
};
static atomic_t num_started;
static struct dst_entry *(*btest_ip6_route_output_flags)(struct net *net,
const struct sock *sk, struct flowi6 *fl6, int flags);
static void btest_account(struct btest_state *state, unsigned seq) {
unsigned bit;
if (state->first) {
state->first = 0;
if (seq > 10000) state->unable = 1;
state->maxseq = seq;
return;
}
if (state->unable) return;
// printk("got seq: %u\n", seq);
if (seq > state->maxseq) {
unsigned c, i, j;
state->lastseq = seq;
c = (seq - state->maxseq) / BTEST_BIT_COUNT;
if ((seq - state->maxseq) % BTEST_BIT_COUNT) ++c;
state->maxseq += c * BTEST_BIT_COUNT;
// printk("shift by: %u\n", c * BTEST_BIT_COUNT);
if (c > BTEST_MASK_LEN) {
state->info.lost += (c - BTEST_MASK_LEN) * BTEST_BIT_COUNT;
c = BTEST_MASK_LEN;
}
for (i = 0; i < c; ++i) {
for (j = 0; j < BTEST_BIT_COUNT; ++j) {
if (!(state->mask[i] & (1 << j))) {
++state->info.lost;
}
}
}
for (i = 0; i < BTEST_MASK_LEN; ++i) {
if (i < BTEST_MASK_LEN - c) state->mask[i] = state->mask[i + c];
else state->mask[i] = 0;
}
}
bit = state->maxseq - seq;
// printk("in range: %u\n", bit);
if (bit < BTEST_MASK_LEN * BTEST_BIT_COUNT) {
unsigned c = BTEST_MASK_LEN - 1 - bit / BTEST_BIT_COUNT;
bit %= BTEST_BIT_COUNT;
if (state->mask[c] & (1 << bit)) {
++state->info.duplicate;
}
else {
state->mask[c] |= (1 << bit);
if (seq < state->lastseq) {
++state->info.outoforder;
}
}
}
}
static void btest_data_ready(struct sock *sk) {
struct btest_state *state = (struct btest_state *) sk->sk_user_data;
int err;
struct sk_buff *skb;
unsigned seq;
int off;
if (!state->started) {
return;
}
// printk("btest_data_ready: %u\n", bytes);
while ((skb = __skb_recv_udp(sk, 0, 1, &off, &err)) == NULL) {
if (err == -EAGAIN) {
printk("btest: no data available?!");
return;
}
printk("btest: recvfrom() error %d\n", -err);
}
seq = ntohl(get_unaligned((unsigned *)(udp_hdr(skb) + 1)));
// seq = ntohl(get_unaligned((unsigned *)(skb->data + sizeof(udp_hdr))));
spin_lock_bh(&state->lock);
btest_account(state, seq);
state->info.received += skb->len;
spin_unlock_bh(&state->lock);
skb_free_datagram(sk, skb);
}
static unsigned x;
static void btest_random(void *buf, int len) {
unsigned char *data = buf;
int i;
for (i = 0; i < len; ++i) {
x += (x >> 26) | (x << 14);
data[i] = x;
}
}
static inline int do_ip_send(struct net *net, struct sock *sock, struct sk_buff *skb) {
return dst_output(net, sock, skb);
// return ip_send(skb);
}
static int btest_send_ipv4(struct sock *sk, unsigned seq, bool random, unsigned size) {
struct inet_sock *inet = inet_sk(sk);
int err = 0;
struct rtable *rt = NULL;
u8 tos = RT_TOS(inet->tos);
unsigned headroom;
struct sk_buff *skb;
struct iphdr *iph;
struct udphdr *uh;
struct flowi4 fl4;
rt = (struct rtable*)sk_dst_check(sk, 0);
if (rt == NULL) {
rt = ip_route_output_ports(&init_net, &fl4, NULL,
inet->inet_daddr, inet->inet_saddr,
inet->inet_dport, inet->inet_sport,
sk->sk_protocol,
RT_CONN_FLAGS(sk),
sk->sk_bound_dev_if);
if (IS_ERR(rt)) return PTR_ERR(rt);
err = -EACCES;
if (rt->rt_flags & RTCF_BROADCAST && !sock_flag(sk, SOCK_BROADCAST)) {
goto out;
}
sk_dst_set(sk, dst_clone(&rt->dst));
}
headroom = LL_RESERVED_SPACE(rt->dst.dev);
skb = alloc_skb(headroom + size + 31, GFP_KERNEL);
if (!skb) {
return -ENOMEM;
}
skb_reserve(skb, headroom);
skb->priority = sk->sk_priority;
skb_dst_set(skb, dst_clone(&rt->dst));
skb_reset_network_header(skb);
iph = (struct iphdr *)skb_put(skb, size);
iph->version = 4;
iph->ihl = sizeof(struct iphdr)>>2;
iph->frag_off = 0;
iph->ttl = 64;
iph->protocol = sk->sk_protocol;
iph->tos = tos;
iph->daddr = inet->inet_daddr;
iph->saddr = inet->inet_saddr;
iph->check = 0;
iph->tot_len = htons(size);
ip_select_ident(&init_net, skb, NULL);
ip_send_check(iph);
skb_set_transport_header(skb, sizeof(struct iphdr));
uh = udp_hdr(skb);
uh->source = inet->inet_sport;
uh->dest = inet->inet_dport;
uh->len = htons(size - sizeof(struct iphdr));
uh->check = 0;
if (size >= 32) {
put_unaligned(htonl(++seq), (unsigned *)(skb->data + 28));
}
if (random) {
btest_random(skb->data + 32, (int)size - 32);
}
err = NF_HOOK(PF_INET, NF_INET_LOCAL_OUT, &init_net,
NULL, skb, NULL, rt->dst.dev, do_ip_send);
out:
ip_rt_put(rt);
// if (err < 0) printk("err: %d\n", err);
return err;
}
static int btest_send_ipv6(struct sock *sk, unsigned seq, bool random, unsigned size) {
struct inet_sock *inet = inet_sk(sk);
int err = 0;
unsigned headroom;
struct sk_buff *skb;
struct ipv6hdr *iph;
struct udphdr *uh;
struct dst_entry *dst = sk_dst_check(sk, 0);
unsigned udpsize = size - sizeof(struct ipv6hdr);
if (!dst) {
struct flowi6 fl = {};
fl.flowi6_oif = sk->sk_bound_dev_if;
fl.daddr = sk->sk_v6_daddr;
fl.saddr = inet->pinet6->saddr;
fl.flowlabel = inet->pinet6->flow_label;
fl.flowi6_proto = sk->sk_protocol;
fl.fl6_sport = inet->inet_sport;
fl.fl6_dport = inet->inet_dport;
dst = btest_ip6_route_output_flags(&init_net, NULL, &fl, 0);
if (dst->error) {
dst_release(dst);
return -EACCES;
}
dst = xfrm_lookup(&init_net, dst, flowi6_to_flowi(&fl), NULL, 0);
if (IS_ERR(dst)) {
return -EACCES;
}
sk_dst_set(sk, dst_clone(dst));
}
headroom = (((dst->dev->hard_header_len + 31) & ~31));
skb = alloc_skb(headroom + size + 31, GFP_KERNEL);
if (!skb) {
return -ENOMEM;
}
skb_reserve(skb, headroom);
skb->priority = sk->sk_priority;
skb_dst_set(skb, dst_clone(dst));
skb->protocol = htons(ETH_P_IPV6);
skb->dev = dst->dev;
skb_reset_network_header(skb);
iph = (struct ipv6hdr *)skb_put(skb, size);
iph->version = 6;
iph->priority = 0;
iph->flow_lbl[0] = htonl(inet->pinet6->flow_label) >> 16;
iph->flow_lbl[1] = htonl(inet->pinet6->flow_label) >> 8;
iph->flow_lbl[2] = htonl(inet->pinet6->flow_label);
iph->payload_len = htons(size - sizeof(struct ipv6hdr));
iph->nexthdr = sk->sk_protocol;
iph->hop_limit = 64;
iph->saddr = inet->pinet6->saddr;
iph->daddr = sk->sk_v6_daddr;
skb_set_transport_header(skb, sizeof(struct ipv6hdr));
uh = udp_hdr(skb);
if (size >= 52) {
put_unaligned(htonl(++seq), (unsigned *)(skb->data + 48));
}
if (random) {
btest_random(skb->data + 52, (int)size - 52);
}
uh->source = inet->inet_sport;
uh->dest = inet->inet_dport;
uh->len = htons(udpsize);
uh->check = 0;
uh->check = csum_ipv6_magic(&iph->saddr, &iph->daddr,
udpsize, IPPROTO_UDP,
csum_partial(uh, udpsize, 0));
err = NF_HOOK(PF_INET6, NF_INET_LOCAL_OUT, &init_net,
NULL, skb, NULL, dst->dev, do_ip_send);
dst_release(dst);
// if (err < 0) printk("err: %d\n", err);
return err;
}
static int btest_send_packet(struct sock *sk, unsigned seq, bool random, unsigned size) {
struct inet_sock *inet = inet_sk(sk);
if (inet->pinet6) {
if (inet->pinet6->saddr.in6_u.u6_addr32[0] ||
inet->pinet6->saddr.in6_u.u6_addr32[1] ||
inet->pinet6->saddr.in6_u.u6_addr32[2] != ntohl(0xffff)) {
return btest_send_ipv6(sk, seq, random, size);
}
}
return btest_send_ipv4(sk, seq, random, size);
}
static inline unsigned btest_get_size(struct btest_state *state) {
if (state->params.send_size_from != state->params.send_size_to) {
unsigned rnd;
get_random_bytes(&rnd, sizeof(rnd));
return state->params.send_size_from +
(rnd % (state->params.send_size_to -
state->params.send_size_from));
}
else {
return state->params.send_size_from;
}
}
static void btest_adjust(struct btest_state *state) {
unsigned size =
(state->params.send_size_from + state->params.send_size_to) / 2;
unsigned long long x = state->params.send_rate;
do_div(x, size);
if (!x) {
x = 1;
}
state->packet_nsecs = 1000000 / (unsigned)x * 1000;
state->next_tx = ktime_get_ns();
}
static void btest_tx_task(struct work_struct *w) {
struct btest_state *state = container_of(w, struct btest_state, work);
u64 start;
stats_inc(&state->stats, STAT_WORK);
start = ktime_get_ns();
spin_lock_bh(&state->lock);
if (!state->started) {
spin_unlock_bh(&state->lock);
return;
}
u64 curr;
u64 dur;
unsigned c = 0;
struct sock *sk = state->sk;
bool random = state->params.send_random;
while (start > state->next_tx) {
unsigned size = btest_get_size(state);
++state->seq;
unsigned seq = state->seq;
stats_inc(&state->stats, STAT_XMIT);
spin_unlock_bh(&state->lock);
if (btest_send_packet(sk, seq, random, size)) {
stats_inc(&state->stats, STAT_XMIT_FAIL);
spin_lock_bh(&state->lock);
break;
}
++c;
if (c == 10) {
c = 0;
curr = ktime_get_ns();
dur = curr - start;
if (dur > 2000000) {
spin_lock_bh(&state->lock);
break;
}
}
spin_lock_bh(&state->lock);
state->next_tx += state->packet_nsecs;
}
unsigned sl = state->packet_nsecs;
hrtimer_start(&state->timer, ktime_set(0, sl), HRTIMER_MODE_REL);
spin_unlock_bh(&state->lock);
}
static enum hrtimer_restart btest_hrtimer(struct hrtimer *t) {
struct btest_state *state = container_of(t, struct btest_state, timer);
stats_inc(&state->stats, STAT_TIMER);
schedule_work_on(state->cpu, &state->work);
return HRTIMER_NORESTART;
}
static int btest_start(struct file *file) {
struct btest_state *state = (struct btest_state *)file->private_data;
struct socket *socket;
int err;
spin_lock_bh(&state->lock);
#ifdef STATS
memcpy(&state->stats, &stats_btest, sizeof(stats_btest));
sprintf(state->stats.name, "btest%u", state->params.fd);
#endif
if (state->started) {
spin_unlock_bh(&state->lock);
return -EBUSY;
}
// printk("btest: start file %p\n", file);
socket = sockfd_lookup(state->params.fd, &err);
if (!socket) {
spin_unlock_bh(&state->lock);
return err;
}
state->socket = socket;
state->sk = socket->sk;
sock_hold(state->sk);
atomic_inc(&num_started);
if (state->params.flags & BTEST_RECEIVE) {
state->old_data_ready = socket->sk->sk_data_ready;
socket->sk->sk_user_data = state;
mb();
socket->sk->sk_data_ready = btest_data_ready;
// socket->file->private_data = state;
// printk("btest: recv file %p\n", socket->file);
}
if (state->params.flags & BTEST_SEND) {
btest_adjust(state);
// printk("btest: size:%u-%u, bytes/sec:%llu, random:%u, packet_nsecs:%u\n",
// state->params.send_size_from, state->params.send_size_to,
// state->params.send_rate,
// state->params.send_random,
// state->packet_nsecs);
state->cpu = btest_cpu;
btest_cpu = (btest_cpu + 1) % num_online_cpus();
schedule_work_on(state->cpu, &state->work);
}
state->started = true;
spin_unlock_bh(&state->lock);
return 0;
}
static int btest_open(struct inode *inode, struct file *file) {
struct btest_state *state;
// printk("btest: open\n");
if (!capable(CAP_SYS_ADMIN)) return -EBUSY;
state = kmalloc(sizeof(struct btest_state), GFP_KERNEL);
if (!state) return -ENOMEM;
memset(state, 0, sizeof(struct btest_state));
state->first = 1;
memset(state->mask, 0xFF, sizeof(unsigned) * BTEST_MASK_LEN);
INIT_WORK(&state->work, btest_tx_task);
hrtimer_init(&state->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
state->timer.function = btest_hrtimer;
spin_lock_init(&state->lock);
file->private_data = state;
return 0;
}
static int btest_release(struct inode *inode, struct file *file) {
struct btest_state *state = (struct btest_state *)file->private_data;
// printk("btest: release: %p\n", state);
if (state->started) {
state->started = false;
cancel_work_sync(&state->work);
spin_lock_bh(&state->lock);
hrtimer_cancel(&state->timer);
if (state->old_data_ready) {
state->sk->sk_data_ready = state->old_data_ready;
}
sockfd_put(state->socket);
sock_put(state->sk);
spin_unlock_bh(&state->lock);
atomic_dec(&num_started);
}
kfree(state);
if (!atomic_read(&num_started)) {
synchronize_net();
}
return 0;
}
static long btest_ioctl(struct file *file,
unsigned int cmd, unsigned long data) {
struct btest_state *state = (struct btest_state *)file->private_data;
// printk("btest_ioctl: %u %lu\n", cmd, data);
switch (cmd) {
case BTEST_IOCTL_START:
if (copy_from_user(&state->params, (void *)data,
sizeof(struct btest_params))) {
return -EINVAL;
}
return btest_start(file);
case BTEST_IOCTL_GET:
// printk("state->info.lost: %u\n", state->info.lost);
spin_lock_bh(&state->lock);
if (copy_to_user((void *)data, &state->info,
sizeof(struct btest_info))) {
spin_unlock_bh(&state->lock);
return -EINVAL;
}
memset(&state->info, 0, sizeof(struct btest_info));
spin_unlock_bh(&state->lock);
return 0;
case BTEST_IOCTL_ADJUST:
if (copy_from_user(&state->params.send_rate, (void *)data,
sizeof(unsigned long long))) {
return -EINVAL;
}
spin_lock_bh(&state->lock);
btest_adjust(state);
spin_unlock_bh(&state->lock);
// printk("btest: adjust rate: %llu packet_nsecs:%u\n",
// state->params.send_rate, state->packet_nsecs);
return 0;
}
return -ENOTTY;
}
static struct file_operations btest_fops = {
owner: THIS_MODULE,
open: btest_open,
release: btest_release,
unlocked_ioctl: btest_ioctl,
compat_ioctl: btest_ioctl,
};
static struct miscdevice btest_miscdev = {
minor: MISC_DYNAMIC_MINOR,
name: "btest",
fops: &btest_fops,
};
int init_module(void) {
int err;
btest_ip6_route_output_flags = symbol_get(ip6_route_output_flags);
err = misc_register(&btest_miscdev);
if (err) {
printk("btest: failed to register char device\n");
return err;
}
get_random_bytes(&x, sizeof(x));
atomic_set(&num_started, 0);
return 0;
}
void cleanup_module(void) {
misc_deregister(&btest_miscdev);
if (btest_ip6_route_output_flags) {
symbol_put(ip6_route_output_flags);
}
}

32
2025-03-19/btest/btest.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef _LINUX_BTEST_H_
#define _LINUX_BTEST_H_
#include <linux/ioctl.h>
#define BTEST_SEND 0x01
#define BTEST_RECEIVE 0x02
struct btest_params {
int fd;
unsigned flags;
unsigned send_size_from;
unsigned send_size_to;
unsigned long long send_rate;
unsigned send_random;
unsigned padding;
};
struct btest_info {
unsigned long long received;
unsigned lost;
unsigned outoforder;
unsigned duplicate;
unsigned padding;
};
#define BTEST_IOCTL_START _IOW('x', 1, struct btest_params)
#define BTEST_IOCTL_GET _IOR('x', 2, struct btest_info)
#define BTEST_IOCTL_ADJUST _IOW('x', 3, unsigned long long)
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
#MODULE = 440/crypto4xx.ko
crypto4xx_FILES = crypto4xx_core.c crypto4xx_alg.c crypto4xx_sa.c
crypto4xx_PREFIX = drivers/crypto
ifneq ($(KERNELRELEASE),)
EXTRA_CFLAGS := $(CFLAGS)
define moddef
obj-m += $(1).o
ifneq ($(1).o, $(2))
$(1)-y := $(2)
EXTRA_CFLAGS += $($(1)_CFLAGS)
endif
endef
$(foreach m, $(MODULE), $(eval $(call moddef,$(m:.ko=),$($(m:.ko=)_FILES:.c=.o))))
EXTRA_CFLAGS := $(subst -I, -I$(src)/, $(EXTRA_CFLAGS))
else
KERNELDIR := /lib/modules/$$(uname -r)/build
all::
$(MAKE) -C $(KERNELDIR) M=$$(pwd) $@
endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,279 @@
/**
* AMCC SoC PPC4xx Crypto Driver
*
* Copyright (c) 2008 Applied Micro Circuits Corporation.
* All rights reserved. James Hsiao <jhsiao@amcc.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* This is the header file for AMCC Crypto offload Linux device driver for
* use with Linux CryptoAPI.
*/
#ifndef __CRYPTO4XX_CORE_H__
#define __CRYPTO4XX_CORE_H__
#include <crypto/internal/hash.h>
#include <crypto/aead.h>
#define PPC460SX_SDR0_SRST 0x201
#define PPC405EX_SDR0_SRST 0x200
#define PPC460EX_SDR0_SRST 0x201
#define PPC460EX_CE_RESET 0x08000000
#define PPC460SX_CE_RESET 0x20000000
#define PPC405EX_CE_RESET 0x00000008
#define CRYPTO4XX_CRYPTO_PRIORITY 300
#define PPC4XX_LAST_PD 63
#define PPC4XX_NUM_PD 64
#define PPC4XX_LAST_GD 1023
#define PPC4XX_NUM_GD 1024
#define PPC4XX_LAST_SD 63
#define PPC4XX_NUM_SD 64
#define PPC4XX_SD_BUFFER_SIZE 2048
#define PD_ENTRY_INUSE 1
#define PD_ENTRY_FREE 0
#define ERING_WAS_FULL 0xffffffff
#define _4XX_ENABLE_CRYPT 1
#define _4XX_ENABLE_HASH 0
#define _4XX_ENABLE_AEAD 0
struct crypto4xx_device;
struct pd_uinfo {
struct crypto4xx_device *dev;
u32 state;
u32 using_sd;
u32 first_gd; /* first gather discriptor
used by this packet */
u32 num_gd; /* number of gather discriptor
used by this packet */
u32 first_sd; /* first scatter discriptor
used by this packet */
u32 num_sd; /* number of scatter discriptors
used by this packet */
void *sa_va; /* shadow sa, when using cp from ctx->sa */
u32 sa_pa;
void *sr_va; /* state record for shadow sa */
u32 sr_pa;
struct scatterlist *dest_va;
struct crypto_async_request *async_req; /* base crypto request
for this packet */
};
struct crypto4xx_device {
struct crypto4xx_core_device *core_dev;
char *name;
u64 ce_phy_address;
void __iomem *ce_base;
void *pdr; /* base address of packet
descriptor ring */
dma_addr_t pdr_pa; /* physical address used to
program ce pdr_base_register */
void *gdr; /* gather descriptor ring */
dma_addr_t gdr_pa; /* physical address used to
program ce gdr_base_register */
void *sdr; /* scatter descriptor ring */
dma_addr_t sdr_pa; /* physical address used to
program ce sdr_base_register */
void *scatter_buffer_va;
dma_addr_t scatter_buffer_pa;
u32 scatter_buffer_size;
void *shadow_sa_pool; /* pool of memory for sa in pd_uinfo */
dma_addr_t shadow_sa_pool_pa;
void *shadow_sr_pool; /* pool of memory for sr in pd_uinfo */
dma_addr_t shadow_sr_pool_pa;
u32 pdr_tail;
u32 pdr_head;
u32 gdr_tail;
u32 gdr_head;
u32 sdr_tail;
u32 sdr_head;
void *pdr_uinfo;
struct list_head alg_list; /* List of algorithm supported
by this device */
};
struct crypto4xx_core_device {
struct device *device;
struct platform_device *ofdev;
struct crypto4xx_device *dev;
u32 int_status;
u32 irq;
spinlock_t lock;
};
struct crypto4xx_ctx_base {
struct crypto4xx_device *dev;
void *sa_in;
dma_addr_t sa_in_dma_addr;
void *sa_out;
dma_addr_t sa_out_dma_addr;
void *state_record;
dma_addr_t state_record_dma_addr;
u32 offset_to_sr_ptr; /* offset to state ptr, in dynamic sa */
u32 direction;
u32 pd_ctl_len;
u32 pd_ctl;
u32 is_hash;
u32 hash_final;
u32 sa_len;
// used only for ciphers
u32 is_gcm;
u32 ctr_aes;
u32 bypass;
};
struct crypto4xx_cipher_tfm_ctx {
struct crypto4xx_ctx_base base;
struct crypto_skcipher *fallback;
dma_addr_t arc4_state_record_dma_addr;
void *arc4_state_record;
u32 append_icv;
u32 init_arc4;
};
struct crypto4xx_cipher_req_ctx {
};
struct crypto4xx_ahash_tfm_ctx {
struct crypto4xx_device *dev;
struct crypto_ahash *fallback;
u32 digest_size;
u32 block_size;
u32 sa_len;
u8 hash;
u8 hash_mode;
u8 iv[2 * 512];
};
struct crypto4xx_ahash_req_ctx {
struct crypto4xx_ctx_base base;
struct ahash_request fallback_req;
};
struct crypto4xx_alg_common {
u32 type;
union {
struct crypto_alg cipher;
struct ahash_alg hash;
struct aead_alg aead;
} u;
};
struct crypto4xx_alg {
struct list_head entry;
struct crypto4xx_alg_common alg;
struct crypto4xx_device *dev;
};
static inline struct crypto4xx_alg *crypto_alg_to_crypto4xx_alg(
struct crypto_alg *x)
{
switch (x->cra_flags & CRYPTO_ALG_TYPE_MASK) {
case CRYPTO_ALG_TYPE_AHASH:
return container_of(__crypto_ahash_alg(x),
struct crypto4xx_alg, alg.u.hash);
}
return container_of(x, struct crypto4xx_alg, alg.u.cipher);
}
extern int crypto4xx_alloc_sa(struct crypto4xx_ctx_base *ctx, u32 size);
extern void crypto4xx_free_sa(struct crypto4xx_ctx_base *ctx);
extern u32 crypto4xx_alloc_state_record(struct crypto4xx_ctx_base *ctx);
extern u32 get_dynamic_sa_offset_state_ptr_field(struct crypto4xx_ctx_base *ctx);
extern u32 get_dynamic_sa_offset_key_field(struct crypto4xx_ctx_base *ctx);
extern u32 get_dynamic_sa_iv_size(struct crypto4xx_ctx_base *ctx);
extern void crypto4xx_memcpy_le(unsigned int *dst,
const unsigned char *buf, int len);
extern u32 crypto4xx_build_pd(struct crypto_async_request *req,
struct crypto4xx_ctx_base *ctx,
struct scatterlist *src,
struct scatterlist *dst,
unsigned int datalen,
u32 aad_len,
void *iv, u32 iv_len);
extern int crypto4xx_setkey_aes_cbc(struct crypto_ablkcipher *cipher,
const u8 *key, unsigned int keylen);
extern int crypto4xx_setkey_3des_cbc(struct crypto_ablkcipher *cipher,
const u8 *key, unsigned int keylen);
extern int crypto4xx_setkey_3des_ecb(struct crypto_ablkcipher *cipher,
const u8 *key, unsigned int keylen);
extern int crypto4xx_encrypt(struct ablkcipher_request *req);
extern int crypto4xx_decrypt(struct ablkcipher_request *req);
// hashing
extern int crypto4xx_hash_digest(struct ahash_request *req);
extern int crypto4xx_hash_final(struct ahash_request *req);
extern int crypto4xx_hash_update(struct ahash_request *req);
extern int crypto4xx_hash_init(struct ahash_request *req);
extern void crypto4xx_hash_alg_exit(struct crypto_tfm *tfm);
extern int crypto4xx_md5_alg_init(struct crypto_tfm *tfm);
extern int crypto4xx_md5_hmac_init(struct crypto_tfm *tfm);
extern int crypto4xx_md5_hmac_setkey(struct crypto_ahash *hash, const u8 *key,
unsigned int keylen);
extern int crypto4xx_sha1_alg_init(struct crypto_tfm *tfm);
extern int crypto4xx_sha1_hmac_init(struct crypto_tfm *tfm);
extern int crypto4xx_sha1_hmac_setkey(struct crypto_ahash *hash, const u8 *key,
unsigned int keylen);
extern int crypto4xx_sha2_alg_init(struct crypto_tfm *tfm);
extern int crypto4xx_sha2_hmac_init(struct crypto_tfm *tfm);
extern int crypto4xx_sha2_hmac_setkey(struct crypto_ahash *hash,
const u8 *key,
unsigned int keylen);
extern u32 get_dynamic_sa_offset_inner_digest(struct crypto4xx_ctx_base *ctx);
extern u32 get_dynamic_sa_offset_outer_digest(struct crypto4xx_ctx_base *ctx);
extern int crypto4xx_pre_compute_hmac(struct crypto4xx_ahash_tfm_ctx *ctx,
void *key,
unsigned int keylen,
unsigned int bs,
unsigned char ha,
unsigned char digs);
int crypto4xx_setkey_aes_ecb(struct crypto_ablkcipher *cipher,
const u8 *key, unsigned int keylen);
int crypto4xx_setkey_aes_ofb(struct crypto_ablkcipher *cipher,
const u8 *key, unsigned int keylen);
int crypto4xx_setkey_aes_cfb(struct crypto_ablkcipher *cipher,
const u8 *key, unsigned int keylen);
int crypto4xx_setkey_aes_ctr(struct crypto_ablkcipher *cipher,
const u8 *key, unsigned int keylen);
#if _4XX_ENABLE_AEAD
int crypto4xx_setkey_aes_gcm(struct crypto_aead *cipher,
const u8 *key, unsigned int keylen);
int crypto4xx_setkey_aes_ccm(struct crypto_aead *cipher,
const u8 *key, unsigned int keylen);
int crypto4xx_encrypt_aes_gcm(struct aead_request *req);
int crypto4xx_decrypt_aes_gcm(struct aead_request *req);
int crypto4xx_encrypt_aes_ccm(struct aead_request *req);
int crypto4xx_decrypt_aes_ccm(struct aead_request *req);
#endif
int crypto4xx_encrypt_ctr(struct ablkcipher_request *req);
int crypto4xx_decrypt_ctr(struct ablkcipher_request *req);
int crypto4xx_setauthsize_aes(struct crypto_aead *ciper,
unsigned int authsize);
extern u32 get_dynamic_sa_offset_arc4_state_ptr(struct crypto4xx_ctx_base *ctx);
int crypto4xx_setauthsize_aes_ccm(struct crypto_aead *ciper,
unsigned int authsize);
#if _4XX_ENABLE_HASH
/* From crypto/md5.c */
extern unsigned int crypto4xx_sa_hash_tbl[3][6];
#endif
#endif

View file

@ -0,0 +1,298 @@
/**
* AMCC SoC PPC4xx Crypto Driver
*
* Copyright (c) 2008 Applied Micro Circuits Corporation.
* All rights reserved. James Hsiao <jhsiao@amcc.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* This filr defines the register set for Security Subsystem
*/
#ifndef __CRYPTO4XX_REG_DEF_H__
#define __CRYPTO4XX_REG_DEF_H__
/* CRYPTO4XX Register offset */
#define CRYPTO4XX_DESCRIPTOR 0x00000000
#define CRYPTO4XX_CTRL_STAT 0x00000000
#define CRYPTO4XX_SOURCE 0x00000004
#define CRYPTO4XX_DEST 0x00000008
#define CRYPTO4XX_SA 0x0000000C
#define CRYPTO4XX_SA_LENGTH 0x00000010
#define CRYPTO4XX_LENGTH 0x00000014
#define CRYPTO4XX_PE_DMA_CFG 0x00000040
#define CRYPTO4XX_PE_DMA_STAT 0x00000044
#define CRYPTO4XX_PDR_BASE 0x00000048
#define CRYPTO4XX_RDR_BASE 0x0000004c
#define CRYPTO4XX_RING_SIZE 0x00000050
#define CRYPTO4XX_RING_CTRL 0x00000054
#define CRYPTO4XX_INT_RING_STAT 0x00000058
#define CRYPTO4XX_EXT_RING_STAT 0x0000005c
#define CRYPTO4XX_IO_THRESHOLD 0x00000060
#define CRYPTO4XX_GATH_RING_BASE 0x00000064
#define CRYPTO4XX_SCAT_RING_BASE 0x00000068
#define CRYPTO4XX_PART_RING_SIZE 0x0000006c
#define CRYPTO4XX_PART_RING_CFG 0x00000070
#define CRYPTO4XX_PDR_BASE_UADDR 0x00000080
#define CRYPTO4XX_RDR_BASE_UADDR 0x00000084
#define CRYPTO4XX_PKT_SRC_UADDR 0x00000088
#define CRYPTO4XX_PKT_DEST_UADDR 0x0000008c
#define CRYPTO4XX_SA_UADDR 0x00000090
#define CRYPTO4XX_GATH_RING_BASE_UADDR 0x000000A0
#define CRYPTO4XX_SCAT_RING_BASE_UADDR 0x000000A4
#define CRYPTO4XX_SEQ_RD 0x00000408
#define CRYPTO4XX_SEQ_MASK_RD 0x0000040C
#define CRYPTO4XX_SA_CMD_0 0x00010600
#define CRYPTO4XX_SA_CMD_1 0x00010604
#define CRYPTO4XX_STATE_PTR 0x000106dc
#define CRYPTO4XX_STATE_IV 0x00010700
#define CRYPTO4XX_STATE_HASH_BYTE_CNT_0 0x00010710
#define CRYPTO4XX_STATE_HASH_BYTE_CNT_1 0x00010714
#define CRYPTO4XX_STATE_IDIGEST_0 0x00010718
#define CRYPTO4XX_STATE_IDIGEST_1 0x0001071c
#define CRYPTO4XX_DATA_IN 0x00018000
#define CRYPTO4XX_DATA_OUT 0x0001c000
#define CRYPTO4XX_INT_UNMASK_STAT 0x000500a0
#define CRYPTO4XX_INT_MASK_STAT 0x000500a4
#define CRYPTO4XX_INT_EN 0x000500a8
#define CRYPTO4XX_INT_PKA 0x00000002
#define CRYPTO4XX_INT_PDR_DONE 0x00008000
#define CRYPTO4XX_INT_MA_WR_ERR 0x00020000
#define CRYPTO4XX_INT_MA_RD_ERR 0x00010000
#define CRYPTO4XX_INT_PE_ERR 0x00000200
#define CRYPTO4XX_INT_USER_DMA_ERR 0x00000040
#define CRYPTO4XX_INT_SLAVE_ERR 0x00000010
#define CRYPTO4XX_INT_MASTER_ERR 0x00000008
#define CRYPTO4XX_INT_ERROR 0x00030258
#define CRYPTO4XX_INT_CFG 0x000500ac
#define CRYPTO4XX_INT_DESCR_RD 0x000500b0
#define CRYPTO4XX_INT_DESCR_CNT 0x000500b4
#define CRYPTO4XX_INT_TIMEOUT_CNT 0x000500b8
#define CRYPTO4XX_DEVICE_CTRL 0x00060080
#define CRYPTO4XX_DEVICE_ID 0x00060084
#define CRYPTO4XX_DEVICE_INFO 0x00060088
#define CRYPTO4XX_DMA_USER_SRC 0x00060094
#define CRYPTO4XX_DMA_USER_DEST 0x00060098
#define CRYPTO4XX_DMA_USER_CMD 0x0006009C
#define CRYPTO4XX_DMA_CFG 0x000600d4
#define CRYPTO4XX_BYTE_ORDER_CFG 0x000600d8
#define CRYPTO4XX_ENDIAN_CFG 0x000600d8
#define CRYPTO4XX_PRNG_STAT 0x00070000
#define CRYPTO4XX_PRNG_CTRL 0x00070004
#define CRYPTO4XX_PRNG_SEED_L 0x00070008
#define CRYPTO4XX_PRNG_SEED_H 0x0007000c
#define CRYPTO4XX_PRNG_RES_0 0x00070020
#define CRYPTO4XX_PRNG_RES_1 0x00070024
#define CRYPTO4XX_PRNG_RES_2 0x00070028
#define CRYPTO4XX_PRNG_RES_3 0x0007002C
#define CRYPTO4XX_PRNG_LFSR_L 0x00070030
#define CRYPTO4XX_PRNG_LFSR_H 0x00070034
/**
* Initilize CRYPTO ENGINE registers, and memory bases.
*/
#define PPC4XX_PDR_POLL 0x3ff
#define PPC4XX_OUTPUT_THRESHOLD 2
#define PPC4XX_INPUT_THRESHOLD 2
#define PPC4XX_PD_SIZE 6
#define PPC4XX_CTX_DONE_INT 0x2000
/*
* in original kernel source PPC4XX_PD_DONE_INT was 0x8000
* and no interrupts were happening, PPC460EX/EXr/GT manual states that
* register CRYPTO4XX_INT_EN bit 15 (DESC) is:
* "Specified number of Descriptors (163) have
* completed processing. Also occurs if a Descriptor
* has just been processed and there are no more
* input Descriptors available.
* but bit 14 (DESCRD) is described as:
* "Command to the Packet Engine to fetch the next
* Packet Descriptor from the PDR."
* while for INT MASK/UNMASK registers mnemonics DESC for bit 15
* and DESCRD for bit 14 are the same, but the descriptions are swapped
* (probably this is copy paste bug in manual)
*/
#define PPC4XX_PD_DONE_INT 0x4000
#define PPC4XX_BYTE_ORDER 0x22222
#define PPC4XX_INTERRUPT_CLR 0x3ffff
#define PPC4XX_PRNG_CTRL_AUTO_EN 0x3
#define PPC4XX_DC_3DES_EN 1
#define PPC4XX_INT_DESCR_CNT 1
#define PPC4XX_INT_TIMEOUT_CNT 0
#define PPC4XX_INT_CFG 1
/**
* all follow define are ad hoc
*/
#define PPC4XX_RING_RETRY 100
#define PPC4XX_RING_POLL 100
#define PPC4XX_SDR_SIZE PPC4XX_NUM_SD
#define PPC4XX_GDR_SIZE PPC4XX_NUM_GD
/**
* Generic Security Association (SA) with all possible fields. These will
* never likely used except for reference purpose. These structure format
* can be not changed as the hardware expects them to be layout as defined.
* Field can be removed or reduced but ordering can not be changed.
*/
#define CRYPTO4XX_DMA_CFG_OFFSET 0x40
union ce_pe_dma_cfg {
struct {
u32 rsv:7;
u32 dir_host:1;
u32 rsv1:2;
u32 bo_td_en:1;
u32 dis_pdr_upd:1;
u32 bo_sgpd_en:1;
u32 bo_data_en:1;
u32 bo_sa_en:1;
u32 bo_pd_en:1;
u32 rsv2:4;
u32 dynamic_sa_en:1;
u32 pdr_mode:2;
u32 pe_mode:1;
u32 rsv3:5;
u32 reset_sg:1;
u32 reset_pdr:1;
u32 reset_pe:1;
} bf;
u32 w;
} __attribute__((packed));
#define CRYPTO4XX_PDR_BASE_OFFSET 0x48
#define CRYPTO4XX_RDR_BASE_OFFSET 0x4c
#define CRYPTO4XX_RING_SIZE_OFFSET 0x50
union ce_ring_size {
struct {
u32 ring_offset:16;
u32 rsv:6;
u32 ring_size:10;
} bf;
u32 w;
} __attribute__((packed));
#define CRYPTO4XX_RING_CONTROL_OFFSET 0x54
union ce_ring_contol {
struct {
u32 continuous:1;
u32 rsv:5;
u32 ring_retry_divisor:10;
u32 rsv1:4;
u32 ring_poll_divisor:10;
} bf;
u32 w;
} __attribute__((packed));
#define CRYPTO4XX_IO_THRESHOLD_OFFSET 0x60
union ce_io_threshold {
struct {
u32 rsv:6;
u32 output_threshold:10;
u32 rsv1:6;
u32 input_threshold:10;
} bf;
u32 w;
} __attribute__((packed));
#define CRYPTO4XX_GATHER_RING_BASE_OFFSET 0x64
#define CRYPTO4XX_SCATTER_RING_BASE_OFFSET 0x68
union ce_part_ring_size {
struct {
u32 sdr_size:16;
u32 gdr_size:16;
} bf;
u32 w;
} __attribute__((packed));
#define MAX_BURST_SIZE_32 0
#define MAX_BURST_SIZE_64 1
#define MAX_BURST_SIZE_128 2
#define MAX_BURST_SIZE_256 3
/* gather descriptor control length */
struct gd_ctl_len {
u32 len:16;
u32 rsv:14;
u32 done:1;
u32 ready:1;
} __attribute__((packed));
struct ce_gd {
u32 ptr;
struct gd_ctl_len ctl_len;
} __attribute__((packed));
struct sd_ctl {
u32 ctl:30;
u32 done:1;
u32 rdy:1;
} __attribute__((packed));
struct ce_sd {
u32 ptr;
struct sd_ctl ctl;
} __attribute__((packed));
#define PD_PAD_CTL_32 0x10
#define PD_PAD_CTL_64 0x20
#define PD_PAD_CTL_128 0x40
#define PD_PAD_CTL_256 0x80
union ce_pd_ctl {
struct {
u32 pd_pad_ctl:8;
u32 status:8;
u32 next_hdr:8;
u32 rsv:2;
u32 cached_sa:1;
u32 hash_final:1;
u32 init_arc4:1;
u32 rsv1:1;
u32 pe_done:1;
u32 host_ready:1;
} bf;
u32 w;
} __attribute__((packed));
union ce_pd_ctl_len {
struct {
u32 bypass:8;
u32 pe_done:1;
u32 host_ready:1;
u32 rsv:2;
u32 pkt_len:20;
} bf;
u32 w;
} __attribute__((packed));
struct ce_pd {
union ce_pd_ctl pd_ctl;
u32 src;
u32 dest;
u32 sa; /* get from ctx->sa_dma_addr */
u32 sa_len; /* only if dynamic sa is used */
union ce_pd_ctl_len pd_ctl_len;
} __attribute__((packed));
#endif

View file

@ -0,0 +1,222 @@
/**
* AMCC SoC PPC4xx Crypto Driver
*
* Copyright (c) 2008 Applied Micro Circuits Corporation.
* All rights reserved. James Hsiao <jhsiao@amcc.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* @file crypto4xx_sa.c
*
* This file implements the security context
* assoicate format.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mod_devicetable.h>
#include <linux/interrupt.h>
#include <linux/spinlock_types.h>
#include <linux/highmem.h>
#include <linux/scatterlist.h>
#include <linux/crypto.h>
#include <crypto/algapi.h>
#include <crypto/des.h>
#include "crypto4xx_reg_def.h"
#include "crypto4xx_sa.h"
#include "crypto4xx_core.h"
u32 get_dynamic_sa_offset_iv_field(struct crypto4xx_ctx_base *ctx)
{
u32 offset;
union dynamic_sa_contents cts;
if (ctx->direction == DIR_INBOUND)
cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_in))->sa_contents;
else
cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_out))->sa_contents;
offset = cts.bf.key_size
+ cts.bf.inner_size
+ cts.bf.outer_size
+ cts.bf.spi
+ cts.bf.seq_num0
+ cts.bf.seq_num1
+ cts.bf.seq_num_mask0
+ cts.bf.seq_num_mask1
+ cts.bf.seq_num_mask2
+ cts.bf.seq_num_mask3;
return sizeof(struct dynamic_sa_ctl) + offset * 4;
}
u32 get_dynamic_sa_offset_state_ptr_field(struct crypto4xx_ctx_base *ctx)
{
u32 offset;
union dynamic_sa_contents cts;
if (ctx->direction == DIR_INBOUND)
cts.w = ((struct dynamic_sa_ctl *) ctx->sa_in)->sa_contents;
else
cts.w = ((struct dynamic_sa_ctl *) ctx->sa_out)->sa_contents;
offset = cts.bf.key_size
+ cts.bf.inner_size
+ cts.bf.outer_size
+ cts.bf.spi
+ cts.bf.seq_num0
+ cts.bf.seq_num1
+ cts.bf.seq_num_mask0
+ cts.bf.seq_num_mask1
+ cts.bf.seq_num_mask2
+ cts.bf.seq_num_mask3
+ cts.bf.iv0
+ cts.bf.iv1
+ cts.bf.iv2
+ cts.bf.iv3;
return sizeof(struct dynamic_sa_ctl) + offset * 4;
}
u32 get_dynamic_sa_offset_arc4_state_ptr(struct crypto4xx_ctx_base *ctx)
{
u32 offset;
union dynamic_sa_contents cts;
if (ctx->direction == DIR_INBOUND)
cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_in))->sa_contents;
else
cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_out))->sa_contents;
offset = cts.bf.key_size
+ cts.bf.inner_size
+ cts.bf.outer_size
+ cts.bf.spi
+ cts.bf.seq_num0
+ cts.bf.seq_num1
+ cts.bf.seq_num_mask0
+ cts.bf.seq_num_mask1
+ cts.bf.seq_num_mask2
+ cts.bf.seq_num_mask3
+ cts.bf.iv0
+ cts.bf.iv1
+ cts.bf.iv2
+ cts.bf.iv3
+ cts.bf.state_ptr
+ cts.bf.arc4_ij_ptr;
return sizeof(struct dynamic_sa_ctl) + offset * 4;
}
u32 get_dynamic_sa_offset_inner_digest(struct crypto4xx_ctx_base *ctx)
{
u32 offset;
union dynamic_sa_contents cts;
if (ctx->direction == DIR_INBOUND)
cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_in))->sa_contents;
else
cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_out))->sa_contents;
offset = cts.bf.key_size;
return sizeof(struct dynamic_sa_ctl) + offset * 4;
}
u32 get_dynamic_sa_offset_outer_digest(struct crypto4xx_ctx_base *ctx)
{
u32 offset;
union dynamic_sa_contents cts;
if (ctx->direction == DIR_INBOUND)
cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_in))->sa_contents;
else
cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_out))->sa_contents;
offset = cts.bf.key_size
+ cts.bf.inner_size;
return sizeof(struct dynamic_sa_ctl) + offset * 4;
}
u32 get_dynamic_sa_offset_spi(struct crypto4xx_ctx_base *ctx)
{
u32 offset;
union dynamic_sa_contents cts;
if (ctx->direction == DIR_INBOUND)
cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_in))->sa_contents;
else
cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_out))->sa_contents;
offset = cts.bf.key_size
+ cts.bf.inner_size
+ cts.bf.outer_size;
return sizeof(struct dynamic_sa_ctl) + offset * 4;
}
u32 get_dynamic_sa_offset_seq_num(struct crypto4xx_ctx_base *ctx)
{
u32 offset;
union dynamic_sa_contents cts;
if (ctx->direction == DIR_INBOUND)
cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_in))->sa_contents;
else
cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_out))->sa_contents;
offset = cts.bf.key_size
+ cts.bf.inner_size
+ cts.bf.outer_size
+ cts.bf.spi;
return sizeof(struct dynamic_sa_ctl) + offset * 4;
}
u32 get_dynamic_sa_offset_seq_num_mask(struct crypto4xx_ctx_base *ctx)
{
u32 offset;
union dynamic_sa_contents cts;
if (ctx->direction == DIR_INBOUND)
cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_in))->sa_contents;
else
cts.w = ((struct dynamic_sa_ctl *)(ctx->sa_out))->sa_contents;
offset = cts.bf.key_size
+ cts.bf.inner_size
+ cts.bf.outer_size
+ cts.bf.spi
+ cts.bf.seq_num0
+ cts.bf.seq_num1;
return sizeof(struct dynamic_sa_ctl) + offset * 4;
}
u32 get_dynamic_sa_iv_size(struct crypto4xx_ctx_base *ctx)
{
union dynamic_sa_contents cts;
if (ctx->direction == DIR_INBOUND)
cts.w = ((struct dynamic_sa_ctl *) ctx->sa_in)->sa_contents;
else
cts.w = ((struct dynamic_sa_ctl *) ctx->sa_out)->sa_contents;
return (cts.bf.iv0 + cts.bf.iv1 + cts.bf.iv2 + cts.bf.iv3) * 4;
}
u32 get_dynamic_sa_offset_key_field(struct crypto4xx_ctx_base *ctx)
{
union dynamic_sa_contents cts;
if (ctx->direction == DIR_INBOUND)
cts.w = ((struct dynamic_sa_ctl *) ctx->sa_in)->sa_contents;
else
cts.w = ((struct dynamic_sa_ctl *) ctx->sa_out)->sa_contents;
return sizeof(struct dynamic_sa_ctl);
}

View file

@ -0,0 +1,566 @@
/**
* AMCC SoC PPC4xx Crypto Driver
*
* Copyright (c) 2008 Applied Micro Circuits Corporation.
* All rights reserved. James Hsiao <jhsiao@amcc.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* This file defines the security context
* assoicate format.
*/
#ifndef __CRYPTO4XX_SA_H__
#define __CRYPTO4XX_SA_H__
#define AES_IV_SIZE 16
/**
* Contents of Dynamic Security Association (SA) with all possible fields
*/
union dynamic_sa_contents {
struct {
u32 arc4_state_ptr:1;
u32 arc4_ij_ptr:1;
u32 state_ptr:1;
u32 iv3:1;
u32 iv2:1;
u32 iv1:1;
u32 iv0:1;
u32 seq_num_mask3:1;
u32 seq_num_mask2:1;
u32 seq_num_mask1:1;
u32 seq_num_mask0:1;
u32 seq_num1:1;
u32 seq_num0:1;
u32 spi:1;
u32 outer_size:5;
u32 inner_size:5;
u32 key_size:4;
u32 cmd_size:4;
} bf;
u32 w;
} __attribute__((packed));
#define SA_OPCODE_ESP 0
#define SA_OPCODE_AH 1
#define SA_OPCODE_SSL 4
#define SA_OPCODE_TLS 5
#define SA_OPCODE_SRTP 7
#define SA_OPCODE_DTLS 1
#define SA_OPCODE_TLS1_1 6
#define SA_OP_GROUP_BASIC 0
#define SA_OP_GROUP_PROTOCOL 1
#define SA_OP_GROUP_EXTEND_PROTOCOL 3
#define SA_OPCODE_EXT_PROT_DTLS 1
#define SA_OPCODE_EXT_PROT_MACSEC 2
#define SA_OPCODE_EXT_PROT_SSL 4
#define SA_OPCODE_EXT_PROT_TLS10 5
#define SA_OPCODE_EXT_PROT_TLS11 6
#define DIR_OUTBOUND 0
#define DIR_INBOUND 1
#define SA_OPCODE_ENCRYPT 0
#define SA_OPCODE_DECRYPT 0
#define SA_OPCODE_ENCRYPT_HASH 1
#define SA_OPCODE_HASH_DECRYPT 1
#define SA_OPCODE_HASH 3
#define SA_OPCODE_HASH_ENCRYPT 4
#define SA_OPCODE_DECRYPT_HASH 4
#define SA_OPCODE_ESP 0
#define SA_OPCODE_AH 1
#define SA_OPCODE_SSL 4
#define SA_OPCODE_TLS 5
#define SA_OPCODE_SRTP 7
#define SA_OPCODE_DTLS 1
#define SA_OPCODE_TLS1_1 6
#define SA_CIPHER_ALG_DES 0
#define SA_CIPHER_ALG_3DES 1
#define SA_CIPHER_ALG_ARC4 2
#define SA_CIPHER_ALG_AES 3
#define SA_CIPHER_ALG_KASUMI 4
#define SA_CIPHER_ALG_NULL 15
#define SA_HASH_ALG_MD5 0
#define SA_HASH_ALG_SHA1 1
#define SA_HASH_ALG_SHA224 2
#define SA_HASH_ALG_SHA256 3
#define SA_HASH_ALG_SHA384 4
#define SA_HASH_ALG_SHA512 5
#define HASH_ALG_MAX_CNT 6
#define SA_HASH_ALG_AES_XCBC_MAC_128 8
#define SA_HASH_ALG_KASUMI_f9 9
#define SA_HASH_ALG_GHASH 12
#define SA_HASH_ALG_GMAC 13
#define SA_HASH_ALG_CBC_MAC 14
#define SA_HASH_ALG_NULL 15
#define SA_LOAD_HASH_FROM_SA 0
#define SA_LOAD_HASH_FROM_STATE 2
#define SA_NOT_LOAD_HASH 3
#define SA_LOAD_IV_FROM_SA 0
#define SA_LOAD_IV_FROM_INPUT 1
#define SA_LOAD_IV_FROM_STATE 2
#define SA_LOAD_IV_GEN_IV 3
#define SA_PAD_TYPE_CONSTANT 2
#define SA_PAD_TYPE_ZERO 3
#define SA_PAD_TYPE_TLS 5
#define SA_PAD_TYPE_DTLS 5
#define SA_NOT_SAVE_HASH 0
#define SA_SAVE_HASH 1
#define SA_NOT_SAVE_IV 0
#define SA_SAVE_IV 1
#define SA_HEADER_PROC 1
#define SA_NO_HEADER_PROC 0
#define SA_HASH_ALG_MD5_DIGEST_SIZE 16
#define SA_HASH_ALG_SHA1_DIGEST_SIZE 20
#define SA_HASH_ALG_SHA224_DIGEST_SIZE 28
#define SA_HASH_ALG_SHA256_DIGEST_SIZE 32
#define SA_HASH_ALG_SHA384_DIGEST_SIZE 48
#define SA_HASH_ALG_SHA512_DIGEST_SIZE 64
#define CRYPTO4XX_MAC_ALGS { "md5", "sha1", \
"sha224", "sha256", "sha384", "sha512" }
union sa_command_0 {
struct {
u32 scatter:1;
u32 gather:1;
u32 save_hash_state:1;
u32 save_iv:1;
u32 load_hash_state:2;
u32 load_iv:2;
u32 digest_len:4;
u32 hdr_proc:1;
u32 extend_pad:1;
u32 stream_cipher_pad:1;
u32 rsv:1;
u32 hash_alg:4;
u32 cipher_alg:4;
u32 pad_type:2;
u32 op_group:2;
u32 dir:1;
u32 opcode:3;
} bf;
u32 w;
} __attribute__((packed));
#define CRYPTO_MODE_ECB 0
#define CRYPTO_MODE_KASUMI 0
#define CRYPTO_MODE_CBC 1
#define CRYPTO_MODE_OFB 2
#define CRYPTO_MODE_CFB 3
#define CRYPTO_MODE_AES_CTR 4
#define CRYPTO_MODE_KASUMI_f8 4
#define CRYPTO_MODE_AES_ICM 5
#define CRYPTO_FEEDBACK_MODE_NO_FB 0
#define CRYPTO_FEEDBACK_MODE_64BIT_OFB 0
#define CRYPTO_FEEDBACK_MODE_8BIT_CFB 1
#define CRYPTO_FEEDBACK_MODE_1BIT_CFB 2
#define CRYPTO_FEEDBACK_MODE_128BIT_CFB 3
#define SA_AES_KEY_LEN_128 2
#define SA_AES_KEY_LEN_192 3
#define SA_AES_KEY_LEN_256 4
#define SA_REV2 1
/*
* The follow defines bits sa_command_1
* In Basic hash mode this bit define simple hash or hmac.
* In IPsec mode, this bit define muting control.
*/
#define SA_HASH_MODE_HASH 0
#define SA_HASH_MODE_HMAC 1
#define SA_MC_ENABLE 0
#define SA_MC_DISABLE 1
#define SA_NOT_COPY_HDR 0
#define SA_COPY_HDR 1
#define SA_NOT_COPY_PAD 0
#define SA_COPY_PAD 1
#define SA_NOT_COPY_PAYLOAD 0
#define SA_COPY_PAYLOAD 1
#define SA_EXTENDED_SN_OFF 0
#define SA_EXTENDED_SN_ON 1
#define SA_SEQ_MASK_OFF 0
#define SA_SEQ_MASK_ON 1
union sa_command_1 {
struct {
u32 crypto_mode31:1;
u32 save_arc4_state:1;
u32 arc4_stateful:1;
u32 key_len:5;
u32 hash_crypto_offset:8;
u32 sa_rev:2;
u32 byte_offset:1;
u32 hmac_muting:1;
u32 feedback_mode:2;
u32 crypto_mode9_8:2;
u32 extended_seq_num:1;
u32 seq_num_mask:1;
u32 mutable_bit_proc:1;
u32 ip_version:1;
u32 copy_pad:1;
u32 copy_payload:1;
u32 copy_hdr:1;
u32 rsv1:1;
} bf;
u32 w;
} __attribute__((packed));
struct dynamic_sa_ctl {
u32 sa_contents;
union sa_command_0 sa_command_0;
union sa_command_1 sa_command_1;
} __attribute__((packed));
/**
* State Record for Security Association (SA)
*/
struct sa_state_record {
u32 save_iv[4];
u32 save_hash_byte_cnt[2];
u32 save_digest[16];
} __attribute__((packed));
/**
* Arc4 State Record for Security Association (SA)
*/
struct arc4_sr {
u32 arc4_state[64];
} __attribute__((packed));
/**
* Security Association (SA) for DES
*/
struct dynamic_sa_des {
struct dynamic_sa_ctl ctrl;
u32 key[2];
u32 iv[2];
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_DES_LEN (sizeof(struct dynamic_sa_des)/4)
#define SA_DES_CONTENTS 0x26000022
/**
* Security Association (SA) for 3DES
*/
struct dynamic_sa_3des {
struct dynamic_sa_ctl ctrl;
u32 key[6];
u32 iv[2]; /* for CBC, OFC, and CFB mode */
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_3DES_LEN (sizeof(struct dynamic_sa_3des)/4)
#define SA_3DES_CONTENTS 0x26000062
/**
* Security Association (SA) for AES128
*
*/
struct dynamic_sa_aes128 {
struct dynamic_sa_ctl ctrl;
u32 key[4];
u32 iv[4]; /* for CBC, OFC, and CFB mode */
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_AES128_LEN (sizeof(struct dynamic_sa_aes128)/4)
#define SA_AES128_CONTENTS 0x3e000042
/**
* Security Association (SA) for AES192
*/
struct dynamic_sa_aes192 {
struct dynamic_sa_ctl ctrl;
u32 key[6];
u32 iv[4]; /* for CBC, OFC, and CFB mode */
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_AES192_LEN (sizeof(struct dynamic_sa_aes192)/4)
#define SA_AES192_CONTENTS 0x3e000062
/**
* Security Association (SA) for AES256
*/
struct dynamic_sa_aes256 {
struct dynamic_sa_ctl ctrl;
u32 key[8];
u32 iv[4]; /* for CBC, OFC, and CFB mode */
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_AES256_LEN (sizeof(struct dynamic_sa_aes256)/4)
#define SA_AES256_CONTENTS 0x3e000082
#define SA_AES_CONTENTS 0x3e000002
/**
* Security Association (SA) for HASH128: HMAC-MD5
*/
struct dynamic_sa_hash128 {
struct dynamic_sa_ctl ctrl;
u32 inner_digest[4];
u32 outer_digest[4];
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_HASH128_LEN (sizeof(struct dynamic_sa_hash128)/4)
#define SA_HASH128_CONTENTS 0x20008402
/**
* Security Association (SA) for HASH160: HMAC-SHA1
*/
struct dynamic_sa_hash160 {
struct dynamic_sa_ctl ctrl;
u32 inner_digest[5];
u32 outer_digest[5];
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_HASH160_LEN (sizeof(struct dynamic_sa_hash160)/4)
#define SA_HASH160_CONTENTS 0x2000a502
/**
* Security Association (SA) for HASH256: HMAC-SHA224, HMAC-SHA256
*/
struct dynamic_sa_hash256 {
struct dynamic_sa_ctl ctrl;
u32 inner_digest[8];
u32 outer_digest[8];
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_HASH256_LEN (sizeof(struct dynamic_sa_hash256)/4)
#define SA_HASH256_CONTENTS 0x20010802
/*
* Security Association (SA) for HASH512: HMAC-SHA512
*/
struct dynamic_sa_hash512 {
struct dynamic_sa_ctl ctrl;
u32 inner_digest[16];
u32 outer_digest[16];
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_HASH512_LEN (sizeof(struct dynamic_sa_hash512)/4)
#define SA_HASH512_CONTENTS 0x20021002
/**
* Security Association (SA) for AES128_XCBC_MAC
*/
struct dynamic_sa_aes128_xcbc_mac {
struct dynamic_sa_ctl ctrl;
u32 key[4];
u32 inner_digest[8];
u32 outer_digest[8];
u32 iv[4]; /* for CBC, OFC, and CFB mode */
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_AES128_XCBC_MAC_LEN (sizeof(struct dynamic_sa_aes128_xcbc_mac)/4)
#define SA_AES128_XCBC_MAC_CONTENTS 0x3e010842
/**
* Security Association (SA) for AES128_GCM
*/
struct dynamic_sa_aes128_gcm {
struct dynamic_sa_ctl ctrl;
u32 key[4];
u32 inner_digest[4];
u32 outer_digest[4];
u32 spi;
u32 seq;
u32 iv[4]; /* for CBC, OFC, and CFB mode */
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_AES128_GCM_LEN (sizeof(struct dynamic_sa_aes128_gcm)/4)
#define SA_AES128_GCM_CONTENTS 0x3e0c8442
/**
* Security Association (SA) for AES192_XCBC_MAC
*/
struct dynamic_sa_aes192_xcbc_mac {
struct dynamic_sa_ctl ctrl;
u32 key[6];
u32 inner_digest[8];
u32 outer_digest[8];
u32 iv[4]; /* for CBC, OFC, and CFB mode */
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_AES192_XCBC_MAC_LEN (sizeof(struct dynamic_sa_aes192_xcbc_mac)/4)
#define SA_AES192_XCBC_MAC_CONTENTS 0x3e010862
/**
* Security Association (SA) for AES192_GCM
*/
struct dynamic_sa_aes192_gcm {
struct dynamic_sa_ctl ctrl;
u32 key[6];
u32 inner_digest[4];
u32 outer_digest[4];
u32 spi;
u32 seq;
u32 iv[4]; /* for CBC, OFC, and CFB mode */
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_AES192_GCM_LEN (sizeof(struct dynamic_sa_aes192_gcm)/4)
#define SA_AES192_GCM_CONTENTS 0x3e0c8462
/**
* Security Association (SA) for AES256_XCBC_MAC
*/
struct dynamic_sa_aes256_xcbc_mac {
struct dynamic_sa_ctl ctrl;
u32 key[8];
u32 inner_digest[8];
u32 outer_digest[8];
u32 iv[4]; /* for CBC, OFC, and CFB mode */
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_AES256_XCBC_MAC_LEN (sizeof(struct dynamic_sa_aes256_xcbc_mac)/4)
#define SA_AES256_XCBC_MAC_CONTENTS 0x3e010882
/**
* Security Association (SA) for AES256_GCM
*/
struct dynamic_sa_aes256_gcm {
struct dynamic_sa_ctl ctrl;
u32 key[8];
u32 inner_digest[4];
u32 outer_digest[4];
u32 spi;
u32 seq;
u32 iv[4]; /* for CBC, OFC, and CFB mode */
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_AES256_GCM_LEN (sizeof(struct dynamic_sa_aes256_gcm)/4)
#define SA_AES256_GCM_CONTENTS 0x3e0c8482
#define SA_AES_GCM_CONTENTS 0x3e0c8402
/**
* Security Association (SA) for Kasumi
*/
struct dynamic_sa_kasumi {
struct dynamic_sa_ctl ctrl;
u32 key[4];
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_KASUMI_LEN (sizeof(struct dynamic_sa_kasumi)/4)
#define SA_KASUMI_CONTENTS 0x20000042
/**
* Security Association (SA) for Kasumi f8
*/
struct dynamic_sa_kasumi_f8 {
struct dynamic_sa_ctl ctrl;
u32 key[4];
u32 iv[2];
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_KASUMI_F8_LEN (sizeof(struct dynamic_sa_kasumi_f8)/4)
#define SA_KASUMI_F8_CONTENTS 0x26000042
#define KASUMI_BLOCK_SIZE 8
#define KASUMI_KEY_SIZE 16
/**
* Security Association (SA) for Kasumi f8
*/
struct dynamic_sa_kasumi_f9 {
struct dynamic_sa_ctl ctrl;
u32 inner_digest[4];
u32 outter_digest[3];
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_KASUMI_F9_LEN (sizeof(struct dynamic_sa_kasumi_f9)/4)
#define SA_KASUMI_F9_CONTENTS 0x20006402
/**
* Security Association (SA) for AES256 CCM
*/
struct dynamic_sa_aes256_ccm {
struct dynamic_sa_ctl ctrl;
u32 key[8];
u32 iv[4]; /* for CBC, OFC, and CFB mode */
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_AES256_CCM_LEN (sizeof(struct dynamic_sa_aes256_ccm)/4)
#define SA_AES256_CCM_CONTENTS 0x3e000082
#define SA_AES_CCM_CONTENTS 0x3e000002
/**
* Security Association (SA) for AES192 CCM
*/
struct dynamic_sa_aes192_ccm {
struct dynamic_sa_ctl ctrl;
u32 key[6];
u32 iv[4]; /* for CBC, OFC, and CFB mode */
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_AES192_CCM_LEN (sizeof(struct dynamic_sa_aes192_ccm)/4)
#define SA_AES192_CCM_CONTENTS 0x3e000062
/**
* Security Association (SA) for AES128 CCM
*/
struct dynamic_sa_aes128_ccm {
struct dynamic_sa_ctl ctrl;
u32 key[4];
u32 iv[4]; /* for CBC, OFC, and CFB mode */
u32 state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_AES128_CCM_LEN (sizeof(struct dynamic_sa_aes128_ccm)/4)
#define SA_AES128_CCM_CONTENTS 0x3e000042
/**
* Security Association (SA) for ARC4
*/
struct arc4_ij_ptr {
u32 rsv:16;
u32 j:8;
u32 i:8;
} __attribute__((packed));
struct dynamic_sa_arc4 {
struct dynamic_sa_ctl ctrl;
u32 key[4];
struct arc4_ij_ptr ij;
u32 arc4_state_ptr;
u32 reserved;
} __attribute__((packed));
#define SA_ARC4_LEN (sizeof(struct dynamic_sa_arc4)/4)
#define SA_ARC4_CONTENTS 0xc0000042
#endif

View file

@ -0,0 +1,29 @@
MODULE = arm/dakota_mmc.ko
dakota_mmc_FILES = sdhci.c sdhci-pltfm.c sdhci-msm.c
dakota_mmc_PREFIX = drivers/mmc/host
ifneq ($(KERNELRELEASE),)
EXTRA_CFLAGS := $(CFLAGS)
define moddef
obj-m += $(1).o
ifneq ($(1).o, $(2))
$(1)-y := $(2)
EXTRA_CFLAGS += $($(1)_CFLAGS)
endif
endef
$(foreach m, $(MODULE), $(eval $(call moddef,$(m:.ko=),$($(m:.ko=)_FILES:.c=.o))))
EXTRA_CFLAGS := $(subst -I, -I$(src)/, $(EXTRA_CFLAGS))
else
KERNELDIR := /lib/modules/$$(uname -r)/build
all::
$(MAKE) -C $(KERNELDIR) M=$$(pwd) $@
endif

View file

@ -0,0 +1,916 @@
/*
* drivers/mmc/host/sdhci-msm.c - Qualcomm SDHCI Platform driver
*
* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/regulator/consumer.h>
#include <linux/delay.h>
#include <linux/mmc/mmc.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <mach/clk-provider-dk.h>
#include "sdhci-pltfm.h"
#define CORE_HC_MODE 0x78
#define HC_MODE_EN 0x1
#define CORE_POWER 0x0
#define CORE_SW_RST BIT(7)
#define CORE_HC_SELECT_IN_EN (1 << 18)
#define CORE_HC_SELECT_IN_MASK (7 << 19)
#define MAX_PHASES 16
#define CORE_DLL_LOCK BIT(7)
#define CORE_DLL_EN BIT(16)
#define CORE_CDR_EN BIT(17)
#define CORE_CK_OUT_EN BIT(18)
#define CORE_CDR_EXT_EN BIT(19)
#define CORE_DLL_PDN BIT(29)
#define CORE_DLL_RST BIT(30)
#define CORE_DLL_CONFIG 0x100
#define CORE_DLL_STATUS 0x108
#define CORE_DLL_CONFIG2 0x1b4
#define CORE_DLL_CLK_DISABLE BIT(21)
#define CORE_VENDOR_SPEC 0x10c
#define CORE_CLK_PWRSAVE BIT(1)
#define VENDOR_CAPS0 0x11c
#define VOLTS_SUPP_1_8V BIT(26)
#define VOLTS_SUPP_3_0V BIT(25)
#define VENDOR_CAPS1 0x120
#define CAPS_SDR_104_SUPPORT BIT(1)
#define CDR_SELEXT_SHIFT 20
#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT)
#define CMUX_SHIFT_PHASE_SHIFT 24
#define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT)
#define SDHCI_RETUNING_MODE BIT(15)
#define SDHCI_ASYNC_INT_SUPPORT BIT(29)
#define SDHCI_SYS_BUS_SUPPORT_64_BIT BIT(28)
#define SDHCI_HS_SUPPORT BIT(21)
#define SDHCI_ADMA2_SUPPORT BIT(19)
#define SDHCI_SUPPORT_8_BIT BIT(18)
#define SDHCI_MAX_BLK_LENGTH BIT(16)
#define SDHCI_BASE_SDCLK_FREQ 0xc800
#define SDHCI_TIMEOUT_CLK_FREQ 0xb2
static const u32 tuning_block_64[] = {
0x00ff0fff, 0xccc3ccff, 0xffcc3cc3, 0xeffefffe,
0xddffdfff, 0xfbfffbff, 0xff7fffbf, 0xefbdf777,
0xf0fff0ff, 0x3cccfc0f, 0xcfcc33cc, 0xeeffefff,
0xfdfffdff, 0xffbfffdf, 0xfff7ffbb, 0xde7b7ff7
};
static const u32 tuning_block_128[] = {
0xff00ffff, 0x0000ffff, 0xccccffff, 0xcccc33cc,
0xcc3333cc, 0xffffcccc, 0xffffeeff, 0xffeeeeff,
0xffddffff, 0xddddffff, 0xbbffffff, 0xbbffffff,
0xffffffbb, 0xffffff77, 0x77ff7777, 0xffeeddbb,
0x00ffffff, 0x00ffffff, 0xccffff00, 0xcc33cccc,
0x3333cccc, 0xffcccccc, 0xffeeffff, 0xeeeeffff,
0xddffffff, 0xddffffff, 0xffffffdd, 0xffffffbb,
0xffffbbbb, 0xffff77ff, 0xff7777ff, 0xeeddbb77
};
struct sdhci_msm_host {
struct platform_device *pdev;
void __iomem *core_mem; /* MSM SDCC mapped address */
struct clk *clk; /* main SD/MMC bus clock */
struct clk *pclk; /* SDHC peripheral bus clock */
struct clk *bus_clk; /* SDHC bus voter clock */
struct mmc_host *mmc;
struct sdhci_pltfm_data sdhci_msm_pdata;
bool emulation;
int irq;
};
/* Platform specific tuning */
static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll)
{
u32 wait_cnt = 50;
u8 ck_out_en;
struct mmc_host *mmc = host->mmc;
/* Poll for CK_OUT_EN bit. max. poll time = 50us */
ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) &
CORE_CK_OUT_EN);
while (ck_out_en != poll) {
if (--wait_cnt == 0) {
dev_err(mmc_dev(mmc), "%s: CK_OUT_EN bit is not %d\n",
mmc_hostname(mmc), poll);
return -ETIMEDOUT;
}
udelay(1);
ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) &
CORE_CK_OUT_EN);
}
return 0;
}
static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase)
{
int rc;
static const u8 grey_coded_phase_table[] = {
0x0, 0x1, 0x3, 0x2, 0x6, 0x7, 0x5, 0x4,
0xc, 0xd, 0xf, 0xe, 0xa, 0xb, 0x9, 0x8
};
unsigned long flags;
u32 config;
struct mmc_host *mmc = host->mmc;
spin_lock_irqsave(&host->lock, flags);
config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
config &= ~(CORE_CDR_EN | CORE_CK_OUT_EN);
config |= (CORE_CDR_EXT_EN | CORE_DLL_EN);
writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
/* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '0' */
rc = msm_dll_poll_ck_out_en(host, 0);
if (rc)
goto err_out;
/*
* Write the selected DLL clock output phase (0 ... 15)
* to CDR_SELEXT bit field of DLL_CONFIG register.
*/
config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
config &= ~CDR_SELEXT_MASK;
config |= grey_coded_phase_table[phase] << CDR_SELEXT_SHIFT;
writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
/* Set CK_OUT_EN bit of DLL_CONFIG register to 1. */
writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
| CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG);
/* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */
rc = msm_dll_poll_ck_out_en(host, 1);
if (rc)
goto err_out;
config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
config |= CORE_CDR_EN;
config &= ~CORE_CDR_EXT_EN;
writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
goto out;
err_out:
dev_err(mmc_dev(mmc), "%s: Failed to set DLL phase: %d\n",
mmc_hostname(mmc), phase);
out:
spin_unlock_irqrestore(&host->lock, flags);
return rc;
}
/*
* Find out the greatest range of consecuitive selected
* DLL clock output phases that can be used as sampling
* setting for SD3.0 UHS-I card read operation (in SDR104
* timing mode) or for eMMC4.5 card read operation (in HS200
* timing mode).
* Select the 3/4 of the range and configure the DLL with the
* selected DLL clock output phase.
*/
static int msm_find_most_appropriate_phase(struct sdhci_host *host,
u8 *phase_table, u8 total_phases)
{
int ret;
u8 ranges[MAX_PHASES][MAX_PHASES] = { {0}, {0} };
u8 phases_per_row[MAX_PHASES] = { 0 };
int row_index = 0, col_index = 0, selected_row_index = 0, curr_max = 0;
int i, cnt, phase_0_raw_index = 0, phase_15_raw_index = 0;
bool phase_0_found = false, phase_15_found = false;
struct mmc_host *mmc = host->mmc;
if (!total_phases || (total_phases > MAX_PHASES)) {
dev_err(mmc_dev(mmc), "%s: Invalid argument: total_phases=%d\n",
mmc_hostname(mmc), total_phases);
return -EINVAL;
}
for (cnt = 0; cnt < total_phases; cnt++) {
ranges[row_index][col_index] = phase_table[cnt];
phases_per_row[row_index] += 1;
col_index++;
if ((cnt + 1) == total_phases) {
continue;
/* check if next phase in phase_table is consecutive or not */
} else if ((phase_table[cnt] + 1) != phase_table[cnt + 1]) {
row_index++;
col_index = 0;
}
}
if (row_index >= MAX_PHASES)
return -EINVAL;
/* Check if phase-0 is present in first valid window? */
if (!ranges[0][0]) {
phase_0_found = true;
phase_0_raw_index = 0;
/* Check if cycle exist between 2 valid windows */
for (cnt = 1; cnt <= row_index; cnt++) {
if (phases_per_row[cnt]) {
for (i = 0; i < phases_per_row[cnt]; i++) {
if (ranges[cnt][i] == 15) {
phase_15_found = true;
phase_15_raw_index = cnt;
break;
}
}
}
}
}
/* If 2 valid windows form cycle then merge them as single window */
if (phase_0_found && phase_15_found) {
/* number of phases in raw where phase 0 is present */
u8 phases_0 = phases_per_row[phase_0_raw_index];
/* number of phases in raw where phase 15 is present */
u8 phases_15 = phases_per_row[phase_15_raw_index];
if (phases_0 + phases_15 >= MAX_PHASES)
/*
* If there are more than 1 phase windows then total
* number of phases in both the windows should not be
* more than or equal to MAX_PHASES.
*/
return -EINVAL;
/* Merge 2 cyclic windows */
i = phases_15;
for (cnt = 0; cnt < phases_0; cnt++) {
ranges[phase_15_raw_index][i] =
ranges[phase_0_raw_index][cnt];
if (++i >= MAX_PHASES)
break;
}
phases_per_row[phase_0_raw_index] = 0;
phases_per_row[phase_15_raw_index] = phases_15 + phases_0;
}
for (cnt = 0; cnt <= row_index; cnt++) {
if (phases_per_row[cnt] > curr_max) {
curr_max = phases_per_row[cnt];
selected_row_index = cnt;
}
}
i = (curr_max * 3) / 4;
if (i)
i--;
ret = ranges[selected_row_index][i];
if (ret >= MAX_PHASES) {
ret = -EINVAL;
dev_err(mmc_dev(mmc), "%s: Invalid phase selected=%d\n",
mmc_hostname(mmc), ret);
}
return ret;
}
static inline void msm_cm_dll_set_freq(struct sdhci_host *host)
{
u32 mclk_freq = 0, config;
/* Program the MCLK value to MCLK_FREQ bit field */
if (host->clock <= 112000000)
mclk_freq = 0;
else if (host->clock <= 125000000)
mclk_freq = 1;
else if (host->clock <= 137000000)
mclk_freq = 2;
else if (host->clock <= 150000000)
mclk_freq = 3;
else if (host->clock <= 162000000)
mclk_freq = 4;
else if (host->clock <= 175000000)
mclk_freq = 5;
else if (host->clock <= 187000000)
mclk_freq = 6;
else if (host->clock <= 200000000)
mclk_freq = 7;
config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
config &= ~CMUX_SHIFT_PHASE_MASK;
config |= mclk_freq << CMUX_SHIFT_PHASE_SHIFT;
writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
}
/* Initialize the DLL (Programmable Delay Line) */
static int msm_init_cm_dll(struct sdhci_host *host)
{
struct mmc_host *mmc = host->mmc;
int wait_cnt = 50;
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
/*
* Make sure that clock is always enabled when DLL
* tuning is in progress. Keeping PWRSAVE ON may
* turn off the clock.
*/
writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
& ~CORE_CLK_PWRSAVE), host->ioaddr + CORE_VENDOR_SPEC);
/* Write 1 to DLL_RST bit of DLL_CONFIG register */
writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
| CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG);
/* Write 1 to DLL_PDN bit of DLL_CONFIG register */
writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
| CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG);
msm_cm_dll_set_freq(host);
/* Write 0 to DLL_RST bit of DLL_CONFIG register */
writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
& ~CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG);
/* Write 0 to DLL_PDN bit of DLL_CONFIG register */
writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
& ~CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG);
/* Set DLL_EN bit to 1. */
writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
| CORE_DLL_EN), host->ioaddr + CORE_DLL_CONFIG);
/* Set CK_OUT_EN bit to 1. */
writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
| CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG);
/* Write 0 to DLL_CLOCK_DISABLE bit of DLL_CONFIG_2 register */
writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG2)
& ~CORE_DLL_CLK_DISABLE), host->ioaddr + CORE_DLL_CONFIG2);
/* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */
while (!(readl_relaxed(host->ioaddr + CORE_DLL_STATUS) &
CORE_DLL_LOCK)) {
/* max. wait for 50us sec for LOCK bit to be set */
if (--wait_cnt == 0) {
dev_err(mmc_dev(mmc), "%s: DLL failed to LOCK\n",
mmc_hostname(mmc));
spin_unlock_irqrestore(&host->lock, flags);
return -ETIMEDOUT;
}
udelay(1);
}
spin_unlock_irqrestore(&host->lock, flags);
return 0;
}
static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
{
int tuning_seq_cnt = 3;
u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0;
const u32 *tuning_block_pattern = tuning_block_64;
int size = sizeof(tuning_block_64); /* Pattern size in bytes */
int rc;
struct mmc_host *mmc = host->mmc;
struct mmc_ios ios = host->mmc->ios;
/*
* Tuning is required for SDR104, HS200 and HS400 cards and
* if clock frequency is greater than 100MHz in these modes.
*/
if (host->clock <= 100 * 1000 * 1000 ||
!((ios.timing == MMC_TIMING_MMC_HS200) ||
(ios.timing == MMC_TIMING_UHS_SDR104)))
return 0;
if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) &&
(mmc->ios.bus_width == MMC_BUS_WIDTH_8)) {
tuning_block_pattern = tuning_block_128;
size = sizeof(tuning_block_128);
}
data_buf = kmalloc(size, GFP_KERNEL);
if (!data_buf)
return -ENOMEM;
retry:
/* First of all reset the tuning block */
rc = msm_init_cm_dll(host);
if (rc)
goto out;
phase = 0;
do {
struct mmc_command cmd = { 0 };
struct mmc_data data = { 0 };
struct mmc_request mrq = {
.cmd = &cmd,
.data = &data
};
struct scatterlist sg;
/* Set the phase in delay line hw block */
rc = msm_config_cm_dll_phase(host, phase);
if (rc)
goto out;
cmd.opcode = opcode;
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
data.blksz = size;
data.blocks = 1;
data.flags = MMC_DATA_READ;
data.timeout_ns = NSEC_PER_SEC; /* 1 second */
data.sg = &sg;
data.sg_len = 1;
sg_init_one(&sg, data_buf, size);
memset(data_buf, 0, size);
mmc_wait_for_req(mmc, &mrq);
if (!cmd.error && !data.error &&
!memcmp(data_buf, tuning_block_pattern, size)) {
/* Tuning is successful at this tuning point */
tuned_phases[tuned_phase_cnt++] = phase;
dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
mmc_hostname(mmc), phase);
}
} while (++phase < ARRAY_SIZE(tuned_phases));
if (tuned_phase_cnt) {
rc = msm_find_most_appropriate_phase(host, tuned_phases,
tuned_phase_cnt);
if (rc < 0)
goto out;
else
phase = rc;
/*
* Finally set the selected phase in delay
* line hw block.
*/
rc = msm_config_cm_dll_phase(host, phase);
if (rc)
goto out;
dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n",
mmc_hostname(mmc), phase);
} else {
if (--tuning_seq_cnt)
goto retry;
/* Tuning failed */
dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n",
mmc_hostname(mmc));
rc = -EIO;
}
out:
kfree(data_buf);
return rc;
}
static void sdhci_msm_toggle_cdr(struct sdhci_host *host, bool enable)
{
u32 config;
config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
if (enable) {
config |= CORE_CDR_EN;
config &= ~CORE_CDR_EXT_EN;
writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
} else {
config &= ~CORE_CDR_EN;
config |= CORE_CDR_EXT_EN;
writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
}
}
static struct sdhci_ops sdhci_msm_ops = {
.platform_execute_tuning = sdhci_msm_execute_tuning,
.toggle_cdr = sdhci_msm_toggle_cdr,
};
#define MMC_CAP2_CD_ACTIVE_HIGH (1 << 10)
#define MMC_CAP2_RO_ACTIVE_HIGH (1 << 11)
#define MMC_CAP2_FULL_PWR_CYCLE (1 << 2)
static irqreturn_t cd_irq_handler(int irq, void *data)
{
struct mmc_host *mmc_host = (struct mmc_host *)data;
sdhci_card_event(mmc_host);
mmc_detect_change(mmc_host, msecs_to_jiffies(200));
return (irqreturn_t) IRQ_HANDLED;
}
extern int cd_gpio_pin;
int msm_mmc_of_parse(struct mmc_host *host, struct platform_device *pdev)
{
struct device_node *np;
u32 bus_width;
bool explicit_inv_wp, gpio_inv_wp = false;
enum of_gpio_flags flags;
int len, ret, gpio, irq_number, sd_ldo;
struct sdhci_msm_host *msm_host =
container_of(host, struct sdhci_msm_host, mmc);
if (!host->parent || !host->parent->of_node || !msm_host)
return 0;
np = host->parent->of_node;
/* "bus-width" is translated to MMC_CAP_*_BIT_DATA flags */
if (of_property_read_u32(np, "bus-width", &bus_width) < 0) {
pr_err("\"bus-width\" property is missing, assuming 1 bit.\n");
bus_width = 1;
}
switch (bus_width) {
case 8:
host->caps |= MMC_CAP_8_BIT_DATA;
/* Hosts capable of 8-bit transfers can also do 4 bits */
case 4:
host->caps |= MMC_CAP_4_BIT_DATA;
break;
case 1:
break;
default:
pr_err("Invalid \"bus-width\" value %ud!\n", bus_width);
return -EINVAL;
}
/* f_max is obtained from the optional "max-frequency" property */
of_property_read_u32(np, "max-frequency", &host->f_max);
sd_ldo = of_get_named_gpio(np, "sd-ldo-gpios", 0);
if (gpio_is_valid(sd_ldo)) {
ret = devm_gpio_request(&pdev->dev, sd_ldo, "sd-ldo-gpios");
if (ret) {
dev_err(&pdev->dev,
"failed to request sd-ldo-gpios %d\n",
sd_ldo);
return ret;
}
dev_info(&pdev->dev, "Got SD LDO GPIO #%d\n", sd_ldo);
/* Toggle SD LDO GPIO on Init */
gpio_direction_output(sd_ldo, 1);
gpio_set_value(sd_ldo, 0);
mdelay(100);
gpio_set_value(sd_ldo, 1);
}
/*
* Configure CD and WP pins. They are both by default active low to
* match the SDHCI spec. If GPIOs are provided for CD and / or WP, the
* mmc-gpio helpers are used to attach, configure and use them. If
* polarity inversion is specified in DT, one of MMC_CAP2_CD_ACTIVE_HIGH
* and MMC_CAP2_RO_ACTIVE_HIGH capability-2 flags is set. If the
* "broken-cd" property is provided, the MMC_CAP_NEEDS_POLL capability
* is set. If the "non-removable" property is found, the
* MMC_CAP_NONREMOVABLE capability is set and no card-detection
* configuration is performed.
*/
/* Parse Card Detection */
if (of_find_property(np, "non-removable", &len)) {
host->caps |= MMC_CAP_NONREMOVABLE;
} else {
bool explicit_inv_cd, gpio_inv_cd = false;
// explicit_inv_cd = of_property_read_bool(np, "cd-inverted");
explicit_inv_cd = of_find_property(np, "cd-inverted", NULL) ? true : false;
if (of_find_property(np, "broken-cd", &len))
host->caps |= MMC_CAP_NEEDS_POLL;
gpio = of_get_named_gpio_flags(np, "cd-gpios", 0, &flags);
if (gpio_is_valid(gpio)) {
if (!(flags & OF_GPIO_ACTIVE_LOW))
gpio_inv_cd = true;
cd_gpio_pin = gpio;
// ret = mmc_gpio_request_cd(host, gpio, 0);
irq_number = gpio_to_irq(gpio);
ret = request_irq(irq_number,
cd_irq_handler,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
mmc_hostname(host),
host);
if (ret < 0) {
pr_err("Failed to request CD GPIO #%d: %d!\n",
gpio, ret);
return ret;
} else {
msm_host->irq = irq_number;
pr_info("Got CD GPIO #%d. Requested interrupt #%d.\n"
, gpio, irq_number);
}
}
if (explicit_inv_cd ^ gpio_inv_cd)
host->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH;
}
// /* Parse Write Protection */
// explicit_inv_wp = of_property_read_bool(np, "wp-inverted");
// explicit_inv_wp = of_find_property(np, "wp-inverted", NULL) ? true : false;
//
// gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags);
// if (gpio_is_valid(gpio)) {
// if (!(flags & OF_GPIO_ACTIVE_LOW))
// gpio_inv_wp = true;
//
// ret = mmc_gpio_request_ro(host, gpio);
// if (ret < 0) {
// dev_err(host->parent,
// "Failed to request WP GPIO: %d!\n", ret);
// goto out;
// } else {
// dev_info(host->parent, "Got WP GPIO #%d.\n",
// gpio);
// }
// }
// if (explicit_inv_wp ^ gpio_inv_wp)
// host->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH;
if (of_find_property(np, "cap-sd-highspeed", &len))
host->caps |= MMC_CAP_SD_HIGHSPEED;
if (of_find_property(np, "cap-mmc-highspeed", &len))
host->caps |= MMC_CAP_MMC_HIGHSPEED;
if (of_find_property(np, "sd-uhs-sdr12", &len))
host->caps |= MMC_CAP_UHS_SDR12;
if (of_find_property(np, "sd-uhs-sdr25", &len))
host->caps |= MMC_CAP_UHS_SDR25;
if (of_find_property(np, "sd-uhs-sdr50", &len))
host->caps |= MMC_CAP_UHS_SDR50;
if (of_find_property(np, "sd-uhs-sdr104", &len))
host->caps |= MMC_CAP_UHS_SDR104;
if (of_find_property(np, "sd-uhs-ddr50", &len))
host->caps |= MMC_CAP_UHS_DDR50;
if (of_find_property(np, "cap-power-off-card", &len))
host->caps |= MMC_CAP_POWER_OFF_CARD;
if (of_find_property(np, "cap-sdio-irq", &len))
host->caps |= MMC_CAP_SDIO_IRQ;
if (of_find_property(np, "keep-power-in-suspend", &len))
host->pm_caps |= MMC_PM_KEEP_POWER;
if (of_find_property(np, "enable-sdio-wakeup", &len))
host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
return 0;
return ret;
}
static int sdhci_msm_probe(struct platform_device *pdev)
{
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_msm_host *msm_host;
struct resource *core_memres;
int ret;
u16 host_version;
struct device_node *np = pdev->dev.of_node;
resource_size_t core_mem_size;
const char *name;
msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL);
if (!msm_host)
return -ENOMEM;
if (!np) {
pr_err("failed to acquire device node\n");
return -ENODEV;
}
msm_host->sdhci_msm_pdata.ops = &sdhci_msm_ops;
host = sdhci_pltfm_init(pdev, &msm_host->sdhci_msm_pdata, 0);
if (IS_ERR(host))
return PTR_ERR(host);
pltfm_host = sdhci_priv(host);
pltfm_host->priv = msm_host;
msm_host->mmc = host->mmc;
msm_host->pdev = pdev;
// msm_host->emulation = of_property_read_bool(np, "qca,emulation");
msm_host->emulation = of_find_property(np, "qca,emulation", NULL) ? true : false;
ret = msm_mmc_of_parse(host->mmc, pdev);
if (ret)
goto pltfm_free;
sdhci_get_of_property(pdev);
/* Setup SDCC bus voter clock. */
msm_host->bus_clk = of_clk_get_by_name(pdev->dev.of_node, "bus");
if (!IS_ERR(msm_host->bus_clk)) {
/* Vote for max. clk rate for max. performance */
ret = clk_set_rate_dk(msm_host->bus_clk, INT_MAX);
if (ret)
goto pltfm_free;
ret = clk_prepare_enable_dk(msm_host->bus_clk);
if (ret)
goto pltfm_free;
}
/* Setup main peripheral bus clock */
msm_host->pclk = of_clk_get_by_name(pdev->dev.of_node, "iface");
if (IS_ERR(msm_host->pclk)) {
ret = PTR_ERR(msm_host->pclk);
dev_err(&pdev->dev, "Perpheral clk setup failed (%d)\n", ret);
goto bus_clk_disable;
}
ret = clk_prepare_enable_dk(msm_host->pclk);
if (ret)
goto bus_clk_disable;
/* Setup SDCC clock */
msm_host->clk = of_clk_get_by_name(pdev->dev.of_node, "core");
if (IS_ERR(msm_host->clk)) {
ret = PTR_ERR(msm_host->clk);
dev_err(&pdev->dev, "SDC MMC clk setup failed (%d)\n", ret);
goto pclk_disable;
}
ret = clk_prepare_enable_dk(msm_host->clk);
if (ret)
goto pclk_disable;
core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
// msm_host->core_mem = devm_ioremap_resource(&pdev->dev, core_memres);
core_mem_size = resource_size(core_memres);
name = core_memres->name ? core_memres->name : dev_name(&pdev->dev);
devm_request_mem_region(&pdev->dev, core_memres->start, core_mem_size, name);
msm_host->core_mem = devm_ioremap(&pdev->dev, core_memres->start, core_mem_size);
if (IS_ERR(msm_host->core_mem)) {
pr_err("Failed to remap registers\n");
ret = PTR_ERR(msm_host->core_mem);
goto clk_disable;
}
if (msm_host->emulation) {
/*Set 1.8V,3V support*/
writel_relaxed((readl_relaxed(host->ioaddr + VENDOR_CAPS0)
| VOLTS_SUPP_1_8V
| VOLTS_SUPP_3_0V),
host->ioaddr + VENDOR_CAPS0);
/*Set timeout caps*/
writel_relaxed((readl_relaxed(host->ioaddr + VENDOR_CAPS0)
| 0x32),
host->ioaddr + VENDOR_CAPS0);
/*Set base clock 10 MHz*/
writel_relaxed((readl_relaxed(host->ioaddr + VENDOR_CAPS0)
| ((0xA) << 8)),
host->ioaddr + VENDOR_CAPS0);
/* Remove SDR104 support*/
writel_relaxed((readl_relaxed(host->ioaddr + VENDOR_CAPS1)
& ~(CAPS_SDR_104_SUPPORT)),
host->ioaddr + VENDOR_CAPS1);
}
/* Set missing caps quirks */
host->quirks |= SDHCI_QUIRK_MISSING_CAPS;
/* Enable SDCC supported capabilities */
host->caps = SDHCI_CAN_VDD_300 |
SDHCI_CAN_VDD_180 |
SDHCI_ASYNC_INT_SUPPORT |
SDHCI_SYS_BUS_SUPPORT_64_BIT |
SDHCI_HS_SUPPORT |
SDHCI_ADMA2_SUPPORT |
SDHCI_SUPPORT_8_BIT |
SDHCI_MAX_BLK_LENGTH |
SDHCI_TIMEOUT_CLK_UNIT |
SDHCI_BASE_SDCLK_FREQ |
SDHCI_TIMEOUT_CLK_FREQ;
/* Enable SD card supported modes */
// host->caps1 = SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 |
// SDHCI_SUPPORT_DDR50 | SDHCI_RETUNING_MODE;
/* Reset the core and Enable SDHC mode */
writel_relaxed(readl_relaxed(msm_host->core_mem + CORE_POWER) |
CORE_SW_RST, msm_host->core_mem + CORE_POWER);
/* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */
usleep_range(1000, 5000);
if (readl(msm_host->core_mem + CORE_POWER) & CORE_SW_RST) {
pr_err("Stuck in reset\n");
ret = -ETIMEDOUT;
goto clk_disable;
}
/* Set HC_MODE_EN bit in HC_MODE register */
writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
host->quirks |= SDHCI_QUIRK_SINGLE_POWER_WRITE;
// host->quirks2 |= SDHCI_QUIRK2_USE_MAX_DISCARD_SIZE;
host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
pr_info("Host Version: 0x%x Vendor Version 0x%x\n",
host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >>
SDHCI_VENDOR_VER_SHIFT));
ret = sdhci_add_host(host);
if (msm_host->emulation) {
/* Enable controller */
writel_relaxed(readl_relaxed(msm_host->core_mem + CORE_POWER) |
0x1, msm_host->core_mem + CORE_POWER);
}
if (ret)
goto clk_disable;
return 0;
clk_disable:
clk_disable_unprepare_dk(msm_host->clk);
pclk_disable:
clk_disable_unprepare_dk(msm_host->pclk);
bus_clk_disable:
if (!IS_ERR(msm_host->bus_clk))
clk_disable_unprepare_dk(msm_host->bus_clk);
pltfm_free:
sdhci_pltfm_free(pdev);
return ret;
}
static int sdhci_msm_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct sdhci_msm_host *msm_host = pltfm_host->priv;
int dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) ==
0xffffffff);
if (msm_host->irq)
free_irq(msm_host->irq, msm_host->mmc);
sdhci_remove_host(host, dead);
sdhci_pltfm_free(pdev);
clk_disable_unprepare_dk(msm_host->clk);
clk_disable_unprepare_dk(msm_host->pclk);
if (!IS_ERR(msm_host->bus_clk))
clk_disable_unprepare_dk(msm_host->bus_clk);
return 0;
}
static const struct of_device_id sdhci_msm_dt_match[] = {
{ .compatible = "qcom,sdhci-msm-v4" },
{},
};
static struct platform_driver sdhci_msm_driver = {
.probe = sdhci_msm_probe,
.remove = sdhci_msm_remove,
.driver = {
.name = "sdhci_msm",
.owner = THIS_MODULE,
.of_match_table = sdhci_msm_dt_match,
},
};
static int __init dakota_mmc_module_init(void) {
return platform_driver_register(&sdhci_msm_driver);
}
static void __exit dakota_mmc_module_exit(void) {
platform_driver_unregister(&sdhci_msm_driver);
}
module_init(dakota_mmc_module_init);
module_exit(dakota_mmc_module_exit);
MODULE_DESCRIPTION("Qualcomm Secure Digital Host Controller Interface driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,278 @@
/*
* sdhci-pltfm.c Support for SDHCI platform devices
* Copyright (c) 2009 Intel Corporation
*
* Copyright (c) 2007, 2011 Freescale Semiconductor, Inc.
* Copyright (c) 2009 MontaVista Software, Inc.
*
* Authors: Xiaobo Xie <X.Xie@freescale.com>
* Anton Vorontsov <avorontsov@ru.mvista.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* Supports:
* SDHCI platform devices
*
* Inspired by sdhci-pci.c, by Pierre Ossman
*/
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
#ifdef CONFIG_PPC
#include <asm/machdep.h>
#endif
#include "sdhci-pltfm.h"
unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
return clk_get_rate(pltfm_host->clk);
}
EXPORT_SYMBOL_GPL(sdhci_pltfm_clk_get_max_clock);
static const struct sdhci_ops sdhci_pltfm_ops = {
};
#ifdef CONFIG_OF
static bool sdhci_of_wp_inverted(struct device_node *np)
{
if (of_get_property(np, "sdhci,wp-inverted", NULL) ||
of_get_property(np, "wp-inverted", NULL))
return true;
/* Old device trees don't have the wp-inverted property. */
#ifdef CONFIG_PPC
return machine_is(mpc837x_rdb) || machine_is(mpc837x_mds);
#else
return false;
#endif /* CONFIG_PPC */
}
void sdhci_get_of_property(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
const __be32 *clk;
u32 bus_width;
int size;
if (of_device_is_available(np)) {
if (of_get_property(np, "sdhci,auto-cmd12", NULL))
host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
if (of_get_property(np, "sdhci,1-bit-only", NULL) ||
(of_property_read_u32(np, "bus-width", &bus_width) == 0 &&
bus_width == 1))
host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
if (sdhci_of_wp_inverted(np))
host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
if (of_get_property(np, "broken-cd", NULL))
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
if (of_get_property(np, "no-1-8-v", NULL))
host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc"))
host->quirks |= SDHCI_QUIRK_BROKEN_DMA;
if (of_device_is_compatible(np, "fsl,p2020-esdhc") ||
of_device_is_compatible(np, "fsl,p1010-esdhc") ||
of_device_is_compatible(np, "fsl,t4240-esdhc") ||
of_device_is_compatible(np, "fsl,mpc8536-esdhc"))
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;
clk = of_get_property(np, "clock-frequency", &size);
if (clk && size == sizeof(*clk) && *clk)
pltfm_host->clock = be32_to_cpup(clk);
if (of_find_property(np, "keep-power-in-suspend", NULL))
host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
if (of_find_property(np, "enable-sdio-wakeup", NULL))
host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
}
}
#else
void sdhci_get_of_property(struct platform_device *pdev) {}
#endif /* CONFIG_OF */
EXPORT_SYMBOL_GPL(sdhci_get_of_property);
struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
const struct sdhci_pltfm_data *pdata,
size_t priv_size)
{
struct sdhci_host *host;
struct device_node *np = pdev->dev.of_node;
struct resource *iomem;
int ret;
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!iomem) {
ret = -ENOMEM;
goto err;
}
if (resource_size(iomem) < 0x100)
dev_err(&pdev->dev, "Invalid iomem size!\n");
/* Some PCI-based MFD need the parent here */
if (pdev->dev.parent != &platform_bus && !np)
host = sdhci_alloc_host(pdev->dev.parent,
sizeof(struct sdhci_pltfm_host) + priv_size);
else
host = sdhci_alloc_host(&pdev->dev,
sizeof(struct sdhci_pltfm_host) + priv_size);
if (IS_ERR(host)) {
ret = PTR_ERR(host);
goto err;
}
host->hw_name = dev_name(&pdev->dev);
if (pdata && pdata->ops)
host->ops = pdata->ops;
else
host->ops = &sdhci_pltfm_ops;
if (pdata) {
host->quirks = pdata->quirks;
host->quirks2 = pdata->quirks2;
}
host->irq = platform_get_irq(pdev, 0);
if (!request_mem_region(iomem->start, resource_size(iomem),
mmc_hostname(host->mmc))) {
dev_err(&pdev->dev, "cannot request region\n");
ret = -EBUSY;
goto err_request;
}
host->ioaddr = ioremap(iomem->start, resource_size(iomem));
if (!host->ioaddr) {
dev_err(&pdev->dev, "failed to remap registers\n");
ret = -ENOMEM;
goto err_remap;
}
/*
* Some platforms need to probe the controller to be able to
* determine which caps should be used.
*/
if (host->ops && host->ops->platform_init)
host->ops->platform_init(host);
platform_set_drvdata(pdev, host);
return host;
err_remap:
release_mem_region(iomem->start, resource_size(iomem));
err_request:
sdhci_free_host(host);
err:
dev_err(&pdev->dev, "%s failed %d\n", __func__, ret);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(sdhci_pltfm_init);
void sdhci_pltfm_free(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
iounmap(host->ioaddr);
release_mem_region(iomem->start, resource_size(iomem));
sdhci_free_host(host);
}
EXPORT_SYMBOL_GPL(sdhci_pltfm_free);
int sdhci_pltfm_register(struct platform_device *pdev,
const struct sdhci_pltfm_data *pdata,
size_t priv_size)
{
struct sdhci_host *host;
int ret = 0;
host = sdhci_pltfm_init(pdev, pdata, priv_size);
if (IS_ERR(host))
return PTR_ERR(host);
sdhci_get_of_property(pdev);
ret = sdhci_add_host(host);
if (ret)
sdhci_pltfm_free(pdev);
return ret;
}
EXPORT_SYMBOL_GPL(sdhci_pltfm_register);
int sdhci_pltfm_unregister(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
sdhci_remove_host(host, dead);
sdhci_pltfm_free(pdev);
return 0;
}
EXPORT_SYMBOL_GPL(sdhci_pltfm_unregister);
#ifdef CONFIG_PM
int sdhci_pltfm_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
return sdhci_suspend_host(host);
}
EXPORT_SYMBOL_GPL(sdhci_pltfm_suspend);
int sdhci_pltfm_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
return sdhci_resume_host(host);
}
EXPORT_SYMBOL_GPL(sdhci_pltfm_resume);
const struct dev_pm_ops sdhci_pltfm_pmops = {
.suspend = sdhci_pltfm_suspend,
.resume = sdhci_pltfm_resume,
};
EXPORT_SYMBOL_GPL(sdhci_pltfm_pmops);
#endif /* CONFIG_PM */
//static int __init sdhci_pltfm_drv_init(void)
//{
// pr_info("sdhci-pltfm: SDHCI platform and OF driver helper\n");
//
// return 0;
//}
//module_init(sdhci_pltfm_drv_init);
//
//static void __exit sdhci_pltfm_drv_exit(void)
//{
//}
//module_exit(sdhci_pltfm_drv_exit);
//
//MODULE_DESCRIPTION("SDHCI platform and OF driver helper");
//MODULE_AUTHOR("Intel Corporation");
//MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,122 @@
/*
* Copyright 2010 MontaVista Software, LLC.
*
* Author: Anton Vorontsov <avorontsov@ru.mvista.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _DRIVERS_MMC_SDHCI_PLTFM_H
#define _DRIVERS_MMC_SDHCI_PLTFM_H
#include <linux/clk.h>
#include <linux/platform_device.h>
#include "sdhci.h"
struct sdhci_pltfm_data {
const struct sdhci_ops *ops;
unsigned int quirks;
unsigned int quirks2;
};
struct sdhci_pltfm_host {
struct clk *clk;
void *priv; /* to handle quirks across io-accessor calls */
/* migrate from sdhci_of_host */
unsigned int clock;
u16 xfer_mode_shadow;
unsigned long private[0] ____cacheline_aligned;
};
#ifdef CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
/*
* These accessors are designed for big endian hosts doing I/O to
* little endian controllers incorporating a 32-bit hardware byte swapper.
*/
static inline u32 sdhci_be32bs_readl(struct sdhci_host *host, int reg)
{
return in_be32(host->ioaddr + reg);
}
static inline u16 sdhci_be32bs_readw(struct sdhci_host *host, int reg)
{
return in_be16(host->ioaddr + (reg ^ 0x2));
}
static inline u8 sdhci_be32bs_readb(struct sdhci_host *host, int reg)
{
return in_8(host->ioaddr + (reg ^ 0x3));
}
static inline void sdhci_be32bs_writel(struct sdhci_host *host,
u32 val, int reg)
{
out_be32(host->ioaddr + reg, val);
}
static inline void sdhci_be32bs_writew(struct sdhci_host *host,
u16 val, int reg)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
int base = reg & ~0x3;
int shift = (reg & 0x2) * 8;
switch (reg) {
case SDHCI_TRANSFER_MODE:
/*
* Postpone this write, we must do it together with a
* command write that is down below.
*/
pltfm_host->xfer_mode_shadow = val;
return;
case SDHCI_COMMAND:
sdhci_be32bs_writel(host,
val << 16 | pltfm_host->xfer_mode_shadow,
SDHCI_TRANSFER_MODE);
return;
}
clrsetbits_be32(host->ioaddr + base, 0xffff << shift, val << shift);
}
static inline void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg)
{
int base = reg & ~0x3;
int shift = (reg & 0x3) * 8;
clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift);
}
#endif /* CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER */
extern void sdhci_get_of_property(struct platform_device *pdev);
extern struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
const struct sdhci_pltfm_data *pdata,
size_t priv_size);
extern void sdhci_pltfm_free(struct platform_device *pdev);
extern int sdhci_pltfm_register(struct platform_device *pdev,
const struct sdhci_pltfm_data *pdata,
size_t priv_size);
extern int sdhci_pltfm_unregister(struct platform_device *pdev);
extern unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host);
static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host)
{
return (void *)host->private;
}
#ifdef CONFIG_PM
extern int sdhci_pltfm_suspend(struct device *dev);
extern int sdhci_pltfm_resume(struct device *dev);
extern const struct dev_pm_ops sdhci_pltfm_pmops;
#define SDHCI_PLTFM_PMOPS (&sdhci_pltfm_pmops)
#else
#define SDHCI_PLTFM_PMOPS NULL
#endif
#endif /* _DRIVERS_MMC_SDHCI_PLTFM_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,425 @@
/*
* linux/drivers/mmc/host/sdhci.h - Secure Digital Host Controller Interface driver
*
* Header file for Host Controller registers and I/O accessors.
*
* Copyright (C) 2005-2008 Pierre Ossman, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
#ifndef __SDHCI_HW_H
#define __SDHCI_HW_H
#include <linux/scatterlist.h>
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/io.h>
#include <linux/mmc/sdhci.h>
/*
* Controller registers
*/
#define SDHCI_DMA_ADDRESS 0x00
#define SDHCI_ARGUMENT2 SDHCI_DMA_ADDRESS
#define SDHCI_BLOCK_SIZE 0x04
#define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
#define SDHCI_BLOCK_COUNT 0x06
#define SDHCI_ARGUMENT 0x08
#define SDHCI_TRANSFER_MODE 0x0C
#define SDHCI_TRNS_DMA 0x01
#define SDHCI_TRNS_BLK_CNT_EN 0x02
#define SDHCI_TRNS_AUTO_CMD12 0x04
#define SDHCI_TRNS_AUTO_CMD23 0x08
#define SDHCI_TRNS_READ 0x10
#define SDHCI_TRNS_MULTI 0x20
#define SDHCI_COMMAND 0x0E
#define SDHCI_CMD_RESP_MASK 0x03
#define SDHCI_CMD_CRC 0x08
#define SDHCI_CMD_INDEX 0x10
#define SDHCI_CMD_DATA 0x20
#define SDHCI_CMD_ABORTCMD 0xC0
#define SDHCI_CMD_RESP_NONE 0x00
#define SDHCI_CMD_RESP_LONG 0x01
#define SDHCI_CMD_RESP_SHORT 0x02
#define SDHCI_CMD_RESP_SHORT_BUSY 0x03
#define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff))
#define SDHCI_GET_CMD(c) ((c>>8) & 0x3f)
#define SDHCI_RESPONSE 0x10
#define SDHCI_BUFFER 0x20
#define SDHCI_PRESENT_STATE 0x24
#define SDHCI_CMD_INHIBIT 0x00000001
#define SDHCI_DATA_INHIBIT 0x00000002
#define SDHCI_DOING_WRITE 0x00000100
#define SDHCI_DOING_READ 0x00000200
#define SDHCI_SPACE_AVAILABLE 0x00000400
#define SDHCI_DATA_AVAILABLE 0x00000800
#define SDHCI_CARD_PRESENT 0x00010000
#define SDHCI_WRITE_PROTECT 0x00080000
#define SDHCI_DATA_LVL_MASK 0x00F00000
#define SDHCI_DATA_LVL_SHIFT 20
#define SDHCI_HOST_CONTROL 0x28
#define SDHCI_CTRL_LED 0x01
#define SDHCI_CTRL_4BITBUS 0x02
#define SDHCI_CTRL_HISPD 0x04
#define SDHCI_CTRL_DMA_MASK 0x18
#define SDHCI_CTRL_SDMA 0x00
#define SDHCI_CTRL_ADMA1 0x08
#define SDHCI_CTRL_ADMA32 0x10
#define SDHCI_CTRL_ADMA64 0x18
#define SDHCI_CTRL_8BITBUS 0x20
#define SDHCI_POWER_CONTROL 0x29
#define SDHCI_POWER_ON 0x01
#define SDHCI_POWER_180 0x0A
#define SDHCI_POWER_300 0x0C
#define SDHCI_POWER_330 0x0E
#define SDHCI_BLOCK_GAP_CONTROL 0x2A
#define SDHCI_WAKE_UP_CONTROL 0x2B
#define SDHCI_WAKE_ON_INT 0x01
#define SDHCI_WAKE_ON_INSERT 0x02
#define SDHCI_WAKE_ON_REMOVE 0x04
#define SDHCI_CLOCK_CONTROL 0x2C
#define SDHCI_DIVIDER_SHIFT 8
#define SDHCI_DIVIDER_HI_SHIFT 6
#define SDHCI_DIV_MASK 0xFF
#define SDHCI_DIV_MASK_LEN 8
#define SDHCI_DIV_HI_MASK 0x300
#define SDHCI_PROG_CLOCK_MODE 0x0020
#define SDHCI_CLOCK_CARD_EN 0x0004
#define SDHCI_CLOCK_INT_STABLE 0x0002
#define SDHCI_CLOCK_INT_EN 0x0001
#define SDHCI_TIMEOUT_CONTROL 0x2E
#define SDHCI_SOFTWARE_RESET 0x2F
#define SDHCI_RESET_ALL 0x01
#define SDHCI_RESET_CMD 0x02
#define SDHCI_RESET_DATA 0x04
#define SDHCI_INT_STATUS 0x30
#define SDHCI_INT_ENABLE 0x34
#define SDHCI_SIGNAL_ENABLE 0x38
#define SDHCI_INT_RESPONSE 0x00000001
#define SDHCI_INT_DATA_END 0x00000002
#define SDHCI_INT_BLK_GAP 0x00000004
#define SDHCI_INT_DMA_END 0x00000008
#define SDHCI_INT_SPACE_AVAIL 0x00000010
#define SDHCI_INT_DATA_AVAIL 0x00000020
#define SDHCI_INT_CARD_INSERT 0x00000040
#define SDHCI_INT_CARD_REMOVE 0x00000080
#define SDHCI_INT_CARD_INT 0x00000100
#define SDHCI_INT_ERROR 0x00008000
#define SDHCI_INT_TIMEOUT 0x00010000
#define SDHCI_INT_CRC 0x00020000
#define SDHCI_INT_END_BIT 0x00040000
#define SDHCI_INT_INDEX 0x00080000
#define SDHCI_INT_DATA_TIMEOUT 0x00100000
#define SDHCI_INT_DATA_CRC 0x00200000
#define SDHCI_INT_DATA_END_BIT 0x00400000
#define SDHCI_INT_BUS_POWER 0x00800000
#define SDHCI_INT_ACMD12ERR 0x01000000
#define SDHCI_INT_ADMA_ERROR 0x02000000
#define SDHCI_INT_NORMAL_MASK 0x00007FFF
#define SDHCI_INT_ERROR_MASK 0xFFFF8000
#define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \
SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX)
#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \
SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \
SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \
SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR | \
SDHCI_INT_BLK_GAP)
#define SDHCI_INT_ALL_MASK ((unsigned int)-1)
#define SDHCI_ACMD12_ERR 0x3C
#define SDHCI_HOST_CONTROL2 0x3E
#define SDHCI_CTRL_UHS_MASK 0x0007
#define SDHCI_CTRL_UHS_SDR12 0x0000
#define SDHCI_CTRL_UHS_SDR25 0x0001
#define SDHCI_CTRL_UHS_SDR50 0x0002
#define SDHCI_CTRL_UHS_SDR104 0x0003
#define SDHCI_CTRL_UHS_DDR50 0x0004
#define SDHCI_CTRL_HS_SDR200 0x0005 /* reserved value in SDIO spec */
#define SDHCI_CTRL_VDD_180 0x0008
#define SDHCI_CTRL_DRV_TYPE_MASK 0x0030
#define SDHCI_CTRL_DRV_TYPE_B 0x0000
#define SDHCI_CTRL_DRV_TYPE_A 0x0010
#define SDHCI_CTRL_DRV_TYPE_C 0x0020
#define SDHCI_CTRL_DRV_TYPE_D 0x0030
#define SDHCI_CTRL_EXEC_TUNING 0x0040
#define SDHCI_CTRL_TUNED_CLK 0x0080
#define SDHCI_CTRL_PRESET_VAL_ENABLE 0x8000
#define SDHCI_CAPABILITIES 0x40
#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F
#define SDHCI_TIMEOUT_CLK_SHIFT 0
#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080
#define SDHCI_CLOCK_BASE_MASK 0x00003F00
#define SDHCI_CLOCK_V3_BASE_MASK 0x0000FF00
#define SDHCI_CLOCK_BASE_SHIFT 8
#define SDHCI_MAX_BLOCK_MASK 0x00030000
#define SDHCI_MAX_BLOCK_SHIFT 16
#define SDHCI_CAN_DO_8BIT 0x00040000
#define SDHCI_CAN_DO_ADMA2 0x00080000
#define SDHCI_CAN_DO_ADMA1 0x00100000
#define SDHCI_CAN_DO_HISPD 0x00200000
#define SDHCI_CAN_DO_SDMA 0x00400000
#define SDHCI_CAN_VDD_330 0x01000000
#define SDHCI_CAN_VDD_300 0x02000000
#define SDHCI_CAN_VDD_180 0x04000000
#define SDHCI_CAN_64BIT 0x10000000
#define SDHCI_SUPPORT_SDR50 0x00000001
#define SDHCI_SUPPORT_SDR104 0x00000002
#define SDHCI_SUPPORT_DDR50 0x00000004
#define SDHCI_DRIVER_TYPE_A 0x00000010
#define SDHCI_DRIVER_TYPE_C 0x00000020
#define SDHCI_DRIVER_TYPE_D 0x00000040
#define SDHCI_RETUNING_TIMER_COUNT_MASK 0x00000F00
#define SDHCI_RETUNING_TIMER_COUNT_SHIFT 8
#define SDHCI_USE_SDR50_TUNING 0x00002000
#define SDHCI_RETUNING_MODE_MASK 0x0000C000
#define SDHCI_RETUNING_MODE_SHIFT 14
#define SDHCI_CLOCK_MUL_MASK 0x00FF0000
#define SDHCI_CLOCK_MUL_SHIFT 16
#define SDHCI_CAPABILITIES_1 0x44
#define SDHCI_MAX_CURRENT 0x48
#define SDHCI_MAX_CURRENT_LIMIT 0xFF
#define SDHCI_MAX_CURRENT_330_MASK 0x0000FF
#define SDHCI_MAX_CURRENT_330_SHIFT 0
#define SDHCI_MAX_CURRENT_300_MASK 0x00FF00
#define SDHCI_MAX_CURRENT_300_SHIFT 8
#define SDHCI_MAX_CURRENT_180_MASK 0xFF0000
#define SDHCI_MAX_CURRENT_180_SHIFT 16
#define SDHCI_MAX_CURRENT_MULTIPLIER 4
/* 4C-4F reserved for more max current */
#define SDHCI_SET_ACMD12_ERROR 0x50
#define SDHCI_SET_INT_ERROR 0x52
#define SDHCI_ADMA_ERROR 0x54
/* 55-57 reserved */
#define SDHCI_ADMA_ADDRESS 0x58
/* 60-FB reserved */
#define SDHCI_PRESET_FOR_SDR12 0x66
#define SDHCI_PRESET_FOR_SDR25 0x68
#define SDHCI_PRESET_FOR_SDR50 0x6A
#define SDHCI_PRESET_FOR_SDR104 0x6C
#define SDHCI_PRESET_FOR_DDR50 0x6E
#define SDHCI_PRESET_DRV_MASK 0xC000
#define SDHCI_PRESET_DRV_SHIFT 14
#define SDHCI_PRESET_CLKGEN_SEL_MASK 0x400
#define SDHCI_PRESET_CLKGEN_SEL_SHIFT 10
#define SDHCI_PRESET_SDCLK_FREQ_MASK 0x3FF
#define SDHCI_PRESET_SDCLK_FREQ_SHIFT 0
#define SDHCI_SLOT_INT_STATUS 0xFC
#define SDHCI_HOST_VERSION 0xFE
#define SDHCI_VENDOR_VER_MASK 0xFF00
#define SDHCI_VENDOR_VER_SHIFT 8
#define SDHCI_SPEC_VER_MASK 0x00FF
#define SDHCI_SPEC_VER_SHIFT 0
#define SDHCI_SPEC_100 0
#define SDHCI_SPEC_200 1
#define SDHCI_SPEC_300 2
#define SDHCI_SDR104_NEEDS_TUNING (1<<10) /* SDR104/HS200 needs tuning */
#define SDHCI_QUIRK2_BROKEN_HS200 (1<<6)
#define SDHCI_QUIRK2_PRESET_VALUE_BROKEN (1<<3)
#define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */
#define SDHCI_QUIRK2_HOST_NO_CMD23 (1<<1)
/* The system physically doesn't support 1.8v, even if the host does */
#define SDHCI_QUIRK2_USE_MAX_DISCARD_SIZE (1<<7)
#define SDHCI_QUIRK2_HOST_OFF_CARD_ON (1<<0)
#define SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON (1<<4)
/* Controller has a non-standard host control register */
#define SDHCI_QUIRK2_NO_1_8_V (1<<2)
/*
* End of controller registers.
*/
#define SDHCI_MAX_DIV_SPEC_200 256
#define SDHCI_MAX_DIV_SPEC_300 2046
/*
* Host SDMA buffer boundary. Valid values from 4K to 512K in powers of 2.
*/
#define SDHCI_DEFAULT_BOUNDARY_SIZE (512 * 1024)
#define SDHCI_DEFAULT_BOUNDARY_ARG (ilog2(SDHCI_DEFAULT_BOUNDARY_SIZE) - 12)
struct sdhci_ops {
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
u32 (*read_l)(struct sdhci_host *host, int reg);
u16 (*read_w)(struct sdhci_host *host, int reg);
u8 (*read_b)(struct sdhci_host *host, int reg);
void (*write_l)(struct sdhci_host *host, u32 val, int reg);
void (*write_w)(struct sdhci_host *host, u16 val, int reg);
void (*write_b)(struct sdhci_host *host, u8 val, int reg);
#endif
void (*set_clock)(struct sdhci_host *host, unsigned int clock);
int (*enable_dma)(struct sdhci_host *host);
unsigned int (*get_max_clock)(struct sdhci_host *host);
unsigned int (*get_min_clock)(struct sdhci_host *host);
unsigned int (*get_timeout_clock)(struct sdhci_host *host);
int (*platform_bus_width)(struct sdhci_host *host,
int width);
void (*platform_send_init_74_clocks)(struct sdhci_host *host,
u8 power_mode);
unsigned int (*get_ro)(struct sdhci_host *host);
void (*platform_reset_enter)(struct sdhci_host *host, u8 mask);
void (*platform_reset_exit)(struct sdhci_host *host, u8 mask);
int (*platform_execute_tuning)(struct sdhci_host *host, u32 opcode);
int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
void (*hw_reset)(struct sdhci_host *host);
void (*platform_suspend)(struct sdhci_host *host);
void (*platform_resume)(struct sdhci_host *host);
void (*adma_workaround)(struct sdhci_host *host, u32 intmask);
void (*platform_init)(struct sdhci_host *host);
void (*card_event)(struct sdhci_host *host);
void (*toggle_cdr)(struct sdhci_host *host, bool enable);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
static inline void sdhci_writel(struct sdhci_host *host, u32 val, int reg)
{
if (unlikely(host->ops->write_l))
host->ops->write_l(host, val, reg);
else
writel(val, host->ioaddr + reg);
}
static inline void sdhci_writew(struct sdhci_host *host, u16 val, int reg)
{
if (unlikely(host->ops->write_w))
host->ops->write_w(host, val, reg);
else
writew(val, host->ioaddr + reg);
}
static inline void sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
{
if (unlikely(host->ops->write_b))
host->ops->write_b(host, val, reg);
else
writeb(val, host->ioaddr + reg);
}
static inline u32 sdhci_readl(struct sdhci_host *host, int reg)
{
if (unlikely(host->ops->read_l))
return host->ops->read_l(host, reg);
else
return readl(host->ioaddr + reg);
}
static inline u16 sdhci_readw(struct sdhci_host *host, int reg)
{
if (unlikely(host->ops->read_w))
return host->ops->read_w(host, reg);
else
return readw(host->ioaddr + reg);
}
static inline u8 sdhci_readb(struct sdhci_host *host, int reg)
{
if (unlikely(host->ops->read_b))
return host->ops->read_b(host, reg);
else
return readb(host->ioaddr + reg);
}
#else
static inline void sdhci_writel(struct sdhci_host *host, u32 val, int reg)
{
writel(val, host->ioaddr + reg);
}
static inline void sdhci_writew(struct sdhci_host *host, u16 val, int reg)
{
writew(val, host->ioaddr + reg);
}
static inline void sdhci_writeb(struct sdhci_host *host, u8 val, int reg)
{
writeb(val, host->ioaddr + reg);
}
static inline u32 sdhci_readl(struct sdhci_host *host, int reg)
{
return readl(host->ioaddr + reg);
}
static inline u16 sdhci_readw(struct sdhci_host *host, int reg)
{
return readw(host->ioaddr + reg);
}
static inline u8 sdhci_readb(struct sdhci_host *host, int reg)
{
return readb(host->ioaddr + reg);
}
#endif /* CONFIG_MMC_SDHCI_IO_ACCESSORS */
extern struct sdhci_host *sdhci_alloc_host(struct device *dev,
size_t priv_size);
extern void sdhci_free_host(struct sdhci_host *host);
static inline void *sdhci_priv(struct sdhci_host *host)
{
return (void *)host->private;
}
extern void sdhci_card_detect(struct sdhci_host *host);
extern int sdhci_add_host(struct sdhci_host *host);
extern void sdhci_remove_host(struct sdhci_host *host, int dead);
extern void sdhci_send_command(struct sdhci_host *host,
struct mmc_command *cmd);
#ifdef CONFIG_PM
extern int sdhci_suspend_host(struct sdhci_host *host);
extern int sdhci_resume_host(struct sdhci_host *host);
extern void sdhci_enable_irq_wakeups(struct sdhci_host *host);
#endif
#ifdef CONFIG_PM_RUNTIME
extern int sdhci_runtime_suspend_host(struct sdhci_host *host);
extern int sdhci_runtime_resume_host(struct sdhci_host *host);
#endif
#endif /* __SDHCI_HW_H */

View file

@ -0,0 +1,72 @@
diff -puNrb e2fsprogs-1.46.2/config/config.sub e2fsprogs/config/config.sub
--- e2fsprogs-1.46.2/config/config.sub 2021-03-15 14:20:09.000000000 +0200
+++ e2fsprogs/config/config.sub 2021-12-03 14:02:42.948535474 +0200
@@ -1338,7 +1338,7 @@ case $os in
# The portable systems comes first.
# Each alternative MUST end in a * to match a version number.
# sysv* is not here because it comes later, after sysvr4.
- gnu* | bsd* | mach* | minix* | genix* | ultrix* | irix* \
+ gnu* | bsd* | mach* | minix* | genix* | ultrix* | irix* | musl* \
| *vms* | esix* | aix* | cnk* | sunos | sunos[34]*\
| hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \
| sym* | kopensolaris* | plan9* \
diff -puNrb e2fsprogs-1.46.2/e2fsck/logfile.c e2fsprogs/e2fsck/logfile.c
--- e2fsprogs-1.46.2/e2fsck/logfile.c 2021-03-15 14:20:09.000000000 +0200
+++ e2fsprogs/e2fsck/logfile.c 2021-12-03 14:02:42.964535515 +0200
@@ -77,7 +77,7 @@ static void expand_percent_expression(e2
if ((ch == 'D') || (ch == 'd') || (ch == 'm') || (ch == 'y') ||
(ch == 'Y') ||
(ch == 'T') || (ch == 'H') || (ch == 'M') || (ch == 'S')) {
- tzset();
+// tzset();
tm = (*flags & FLAG_UTC) ? gmtime_r(&ctx->now, &tm_struct) :
localtime_r(&ctx->now, &tm_struct);
}
diff -puNrb e2fsprogs-1.46.2/e2fsck/unix.c e2fsprogs/e2fsck/unix.c
--- e2fsprogs-1.46.2/e2fsck/unix.c 2021-03-15 14:20:09.000000000 +0200
+++ e2fsprogs/e2fsck/unix.c 2021-12-03 14:02:42.968535525 +0200
@@ -241,6 +241,8 @@ static void check_mount(e2fsck_t ctx)
ctx->filesystem_name);
return;
}
+ if (ctx->mount_flags & EXT2_MF_READONLY)
+ return; /* disable root fs filesystem check */
/*
* If the filesystem isn't mounted, or it's the root
@@ -1499,8 +1501,9 @@ restart:
if ((ctx->options & E2F_OPT_READONLY) == 0) {
flags |= EXT2_FLAG_RW;
if (!(ctx->mount_flags & EXT2_MF_ISROOT &&
- ctx->mount_flags & EXT2_MF_READONLY))
- flags |= EXT2_FLAG_EXCLUSIVE;
+ ctx->mount_flags & EXT2_MF_READONLY)) {
+ //flags |= EXT2_FLAG_EXCLUSIVE;
+ }
if ((ctx->mount_flags & EXT2_MF_READONLY) &&
(ctx->options & E2F_OPT_FORCE))
flags &= ~EXT2_FLAG_EXCLUSIVE;
diff -puNrb e2fsprogs-1.46.2/lib/blkid/llseek.c e2fsprogs/lib/blkid/llseek.c
--- e2fsprogs-1.46.2/lib/blkid/llseek.c 2021-03-15 14:20:09.000000000 +0200
+++ e2fsprogs/lib/blkid/llseek.c 2021-12-03 14:02:42.972535536 +0200
@@ -56,7 +56,7 @@ extern long long llseek(int fd, long lon
#else /* SIZEOF_LONG != SIZEOF_LONG_LONG */
-#include <linux/unistd.h>
+//#include <linux/unistd.h>
#ifndef __NR__llseek
#define __NR__llseek 140
diff -puNrb e2fsprogs-1.46.2/lib/ext2fs/llseek.c e2fsprogs/lib/ext2fs/llseek.c
--- e2fsprogs-1.46.2/lib/ext2fs/llseek.c 2021-03-15 14:20:09.000000000 +0200
+++ e2fsprogs/lib/ext2fs/llseek.c 2021-12-03 14:02:42.988535578 +0200
@@ -57,7 +57,7 @@ extern long long llseek (int fd, long lo
#else /* SIZEOF_LONG != SIZEOF_LONG_LONG */
-#include <linux/unistd.h>
+//#include <linux/unistd.h>
#ifndef __NR__llseek
#define __NR__llseek 140

237
2025-03-19/e2fsprogs-1.46.2/.gitignore vendored Normal file
View file

@ -0,0 +1,237 @@
autom4te.cache
build
build.profiled
build.static
FILES
^core
*~
patches
Makefile
*.bak
*.diff
*.dSYM
*.o
*.a
*.orig
*.patch
*.pc
*.rej
*.swp
00[0-9][1-9]*.patch
MCONFIG
asm_types.h
config.log
config.status
cscope.*
debugfs/extent_cmds.c
debugfs/debug_cmds.c
debugfs/debugfs
debugfs/debugfs.8
debugfs/extent_cmds.c
debugfs/tst_set_fields
doc/libext2fs.aux
doc/libext2fs.cp
doc/libext2fs.dvi
doc/libext2fs.fn
doc/libext2fs.fns
doc/libext2fs.info
doc/libext2fs.ky
doc/libext2fs.log
doc/libext2fs.pg
doc/libext2fs.toc
doc/libext2fs.tp
doc/libext2fs.vr
e2fsck/crc32table.h
e2fsck/e2fsck
e2fsck/e2fsck.8
e2fsck/e2fsck.conf.5
e2fsck/e2fsck.shared
e2fsck/e2fsck.static
e2fsck/gen_crc32table
e2fsck/prof_err.c
e2fsck/prof_err.h
e2fsck/tst_crc32
e2fsck/tst_problem
e2fsck/tst_refcount
e2fsck/tst_region
e2fsprogs.spec
ext2ed/ext2ed.conf
ext2ed/ext2ed.8
intl/charset.alias
intl/libgnuintl.h
intl/libintl.a
intl/libintl.h
intl/ref-add.sed
intl/ref-del.sed
lib/blkid/blkid.h
lib/blkid/blkid.pc
lib/blkid/blkid_types.h
lib/blkid/libblkid.3
lib/blkid/subdirs
lib/blkid/test_probe
lib/blkid/tests/*.ok
lib/blkid/tests/*.out
lib/blkid/tests/tmp
lib/blkid/tst_cache
lib/blkid/tst_dev
lib/blkid/tst_devname
lib/blkid/tst_devno
lib/blkid/tst_getsize
lib/blkid/tst_probe
lib/blkid/tst_read
lib/blkid/tst_resolve
lib/blkid/tst_save
lib/blkid/tst_tag
lib/blkid/tst_types
lib/config.h
lib/dirpaths.h
lib/e2p/e2p.pc
lib/e2p/subdirs
lib/e2p/tst_feature
lib/e2p/tst_ostype
lib/et/com_err.pc
lib/et/compile_et
lib/et/subdirs
lib/ext2fs/crc32c_table.h
lib/ext2fs/debug_cmds.c
lib/ext2fs/ext2_err.c
lib/ext2fs/ext2_err.et
lib/ext2fs/ext2_err.h
lib/ext2fs/ext2_types.h
lib/ext2fs/ext2fs.pc
lib/ext2fs/extent_cmds.c
lib/ext2fs/gen_crc32ctable
lib/ext2fs/subdirs
lib/ext2fs/tst_badblocks
lib/ext2fs/tst_bitmaps
lib/ext2fs/tst_bitmaps_cmd.c
lib/ext2fs/tst_bitmaps_out
lib/ext2fs/tst_bitops
lib/ext2fs/tst_cmds.c
lib/ext2fs/tst_csum
lib/ext2fs/tst_crc32c
lib/ext2fs/tst_digest_encode
lib/ext2fs/tst_getsectsize
lib/ext2fs/tst_getsize
lib/ext2fs/tst_icount
lib/ext2fs/tst_inline
lib/ext2fs/tst_inline_data
lib/ext2fs/tst_inode_size
lib/ext2fs/tst_iscan
lib/ext2fs/tst_libext2fs
lib/ext2fs/tst_sha256
lib/ext2fs/tst_sha512
lib/ext2fs/tst_super_size
lib/ext2fs/tst_types
lib/quota/subdirs
lib/ss/mk_cmds
lib/ss/ss.pc
lib/ss/ss_err.c
lib/ss/ss_err.h
lib/ss/std_rqs.c
lib/ss/subdirs
lib/ss/test.diff
lib/ss/test_cmd.c
lib/ss/test_out
lib/ss/test_ss
lib/support/prof_err.c
lib/support/prof_err.h
lib/support/subdirs
lib/uuid/subdirs
lib/uuid/tst_uuid
lib/uuid/uuid.3
lib/uuid/uuid.h
lib/uuid/uuid.pc
lib/uuid/uuid_clear.3
lib/uuid/uuid_compare.3
lib/uuid/uuid_copy.3
lib/uuid/uuid_generate.3
lib/uuid/uuid_is_null.3
lib/uuid/uuid_parse.3
lib/uuid/uuid_time
lib/uuid/uuid_time.3
lib/uuid/uuid_types.h
lib/uuid/uuid_unparse.3
misc/badblocks
misc/badblocks.8
misc/base_device
misc/base_device.out
misc/blkid
misc/blkid.8
misc/chattr
misc/chattr.1
misc/default_profile.c
misc/dumpe2fs
misc/dumpe2fs.8
misc/e2freefrag
misc/e2freefrag.8
misc/e2fuzz
misc/e2image
misc/e2image.8
misc/e2initrd_helper
misc/e2label.8
misc/e2mmpstatus
misc/e2mmpstatus.8
misc/e2undo
misc/e2undo.8
misc/e4crypt
misc/e4crypt.8
misc/e4defrag
misc/e4defrag.8
misc/ext4.5
misc/filefrag
misc/filefrag.8
misc/findfs.8
misc/findsuper
misc/fsck
misc/fsck.8
misc/fuse2fs
misc/fuse2fs.1
misc/logsave
misc/logsave.8
misc/lsattr
misc/lsattr.1
misc/mke2fs
misc/mke2fs.8
misc/mke2fs.conf.5
misc/mke2fs.conf
misc/mklost+found
misc/mklost+found.8
misc/prof_err.c
misc/prof_err.h
misc/profile.h
misc/tune2fs
misc/tune2fs.8
misc/uuidd
misc/uuidd.8
misc/uuidgen
misc/uuidgen.1
ncscope.*
parse-types.log
po/Makefile.in
po/POTFILES
public_config.h
resize/resize2fs
resize/resize2fs.8
resize/test_extent
resize/test_extent.out
tags
TAGS
tests/progs/test_icount
tests/progs/test_icount_cmds.c
tests/progs/crcsum
tests/*.ok
tests/*.failed
tests/*.log
tests/*.tmp
tests/*.slow
tests/test_data.tmp
tests/mke2fs.conf
tests/test_script
tests/test_one
util/dirpaths.h
util/gen-tarball
util/install-symlink
util/subst
util/subst.conf
Meta

View file

@ -0,0 +1,4 @@
#!/bin/sh
find . -type f \! -name \*~ \! -exec grep -q Begin-Header \{\} \; -print \
| grep -v ^./build

View file

@ -0,0 +1,19 @@
1) Do "make depend" if necessary
2) "make check"!!!
3) Use "git log" to assemble release notes and update RELEASE-NOTES symlink.
4) Update files which contain version information
version.h
README
e2fsprogs.lsm
e2fsprogs.spec
doc/libext2fs.texinfo (three places)
5) Run "(cd po; make e2fsprogs.pot-update)" to update the translation template.
6) Make source tarfile
7) Adjust sizes in e2fsprogs-VER.lsm; rebuild source files; regenerate source tarfile

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,28 @@
// Copyright 2017 The Android Open Source Project
cc_defaults {
name: "e2fsprogs-defaults",
cflags: ["-Wall", "-Werror"],
target: {
darwin: {
// Still has unfixed/unsuppressed warnings.
cflags: ["-Wno-error"],
},
windows: {
cflags: [
"-Wno-typedef-redefinition",
"-Wno-unused-parameter",
"-Wno-unused-variable",
],
},
},
}
subdirs = [
"contrib",
"debugfs",
"e2fsck",
"lib",
"misc",
"resize",
]

View file

@ -0,0 +1,52 @@
# Copyright (C) 2007 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# If you don't need to do a full clean build but would like to touch
# a file or delete some intermediate files, add a clean step to the end
# of the list. These steps will only be run once, if they haven't been
# run before.
#
# E.g.:
# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
#
# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
# files that are missing or have been moved.
#
# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
# Use $(OUT_DIR) to refer to the "out" directory.
#
# If you need to re-do something that's already mentioned, just copy
# the command and add it to the bottom of the list. E.g., if a change
# that you made last week required touching a file and a change you
# made today requires touching the same file, just copy the old
# touch step and add it to the end of the list.
#
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
# For example:
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
# ************************************************
# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
# ************************************************
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libext2_uuid_intermediates)
$(call add-clean-step, rm -rf $(TARGET_RECOVERY_OUT)/root/sbin)

View file

@ -0,0 +1,69 @@
To install the second extended file system management programs,
just follow the steps:
1) Change directory into the top of the e2fsprogs source tree
2) Create a build directory and cd into it:
mkdir build; cd build
3) Run the configure script
../configure
If you wish to turn on ELF shared libraries, add the option
--enable-elf-shlibs. If you wish to build profiling libraries, add
the option --enable-profile.
Note that if you are building on an older system (i.e., a 2.4
kernel and/or glibc 2.2), the use of thread local storage will probably
cause programs that use the uuid library to core dump. To disable
thread local storage, use the configure option --disable-tls.
4) Compile the programs
make
5) Check to make sure the installation built correctly:
make check
6) Install the programs
Run `make install'
7) Install the include files and libraries
You can run `make install-libs' to install the include files and
libraries. Please note that this installation is not needed for the
programs to work. It is only needed if you expect to develop other
programs using the libraries or if you want to compile other program
using these libraries (like the 4.4BSD dump and restore port).
8) Remove any pre-formatted man pages.
Some distributions will have pre-formatted manual pages which
will always be displayed in preference to newer man pages in /usr/man.
If this is the case, you may need to manually remove them in order to
see the correct manual pages. The shell script in
install-utils/remove_preformat_manpages may be helpful in doing so.
9) Make sure your /etc/fstab file is correct.
Some distributions install an /etc/fstab which is missing the
fifth and sixth field of filesystem entry, which are the dump
frequency, and the fsck pass number, respectively. The problem with
this is that the getmntent() library routine interprets those missing
fields as "0", and a pass number of 0 is documented as meaning that
fsck should not check that particular filesystem. If your entries in
your /etc/fstab file look like this:
/dev/hda4 / ext2 defaults
you should add "1 1" at the end of each line, so that they look like this:
/dev/hda4 / ext2 defaults 1 1
There is a script in install-utils/convfstab (donated by
Michael Weller) that may help you correct your /etc/fstab file.

View file

@ -0,0 +1,53 @@
NOTE: This is the ELF version of the binary distribution. If you have
a DLL system, please compile e2fsprogs from sources yourself. (In
fact, in general you're better off compiling e2fsprogs from sources
instead of using precompiled binaries.)
Also please note that these binaries assume the use of the GNU Libc.
If you're still using libc5, you'll need build e2fsprogs from source.
To install the binary distribution of the second extended file
system management programs, just follow the steps:
1) Install this tar file using the following command:
gunzip < e2fsprogs-1.13-elfbin.tar.gz | (cd /; tar Sxvpf - )
2) Run ldconfig to update the shared library pointers.
As root, type /sbin/ldconfig. This will update the links to
the shared libraries included in the distribution. You can then remove
the old versions of the libraries from /lib.
3) Remove any pre-formatted man pages.
Some distributions will have pre-formatted manual pages which
will always be displayed in preference to newer man pages in /usr/man.
If this is the case, you may need to manually remove them in order to
see the correct manual pages. The shell script in
install-utils/remove_preformat_manpages may be helpful in doing so.
4) Make sure your /etc/fstab file is correct.
Some distributions install an /etc/fstab which is missing the
fifth and sixth field of filesystem entry, which are the dump
frequency, and the fsck pass number, respectively. The problem with
this is that the getmntent() library routine interprets those missing
fields as "0", and a pass number of 0 is documented as meaning that
fsck should not check that particular filesystem. If your entries in
your /etc/fstab file look like this:
/dev/hda4 / ext2 defaults
you should add "1 1" at the end of each line, so that they look like this:
/dev/hda4 / ext2 defaults 1 1
There is a script in install-utils/convfstab (donated by
Michael Weller) that may help you correct your /etc/fstab file.
5) Cleanup files from the installation.
When you're done with the installation, you will probably want
to remove /INSTALL (this file), /README, and /install-utils from your
root directory

View file

@ -0,0 +1,335 @@
# Beginning of file MCONFIG
all::
all-static::
check::
fullcheck::
SHELL = /bin/sh
COMPRESS_EXT = gz bz2 bz Z
prefix = @prefix@
root_prefix = @root_prefix@
exec_prefix = @exec_prefix@
root_bindir = @root_bindir@
root_sbindir = @root_sbindir@
root_libdir = @root_libdir@
datarootdir = @datarootdir@
bindir = @bindir@
sbindir = @sbindir@
libdir = @libdir@
datadir= @datadir@
localedir = $(datadir)/locale
root_sysconfdir= @root_sysconfdir@
includedir = @includedir@
mandir = @mandir@
man1dir = $(mandir)/man1
man3dir = $(mandir)/man3
man5dir = $(mandir)/man5
man8dir = $(mandir)/man8
infodir = @infodir@
datadir = @datadir@
pkgconfigdir = $(libdir)/pkgconfig
pkglibdir = $(libdir)/e2fsprogs
HAVE_UDEV = @have_udev@
UDEV_RULES_DIR = @pkg_udev_rules_dir@
HAVE_CROND = @have_crond@
CROND_DIR = @crond_dir@
HAVE_SYSTEMD = @have_systemd@
SYSTEMD_SYSTEM_UNIT_DIR = @systemd_system_unit_dir@
@SET_MAKE@
@ifGNUmake@ V =
@ifGNUmake@ ifeq ($(strip $(V)),)
@ifGNUmake@ # E = @echo
@ifGNUmake@ # ES = echo
@ifGNUmake@ # Q = @
@ifGNUmake@ E = @E@
@ifGNUmake@ ES = @ES@
@ifGNUmake@ Q = @Q@
@ifGNUmake@ else
@ifGNUmake@ E = @\#
@ifGNUmake@ ES = \#
@ifGNUmake@ Q =
@ifGNUmake@ endif
@ifNotGNUmake@ E = @E@
@ifNotGNUmake@ ES = @ES@
@ifNotGNUmake@ Q = @Q@
@ifGNUmake@ CHECK=sparse
@ifGNUmake@ CHECK_OPTS=-Wsparse-all -Wno-transparent-union -Wno-return-void -Wno-undef -Wno-non-pointer-null
@ifGNUmake@ CPPCHECK=cppcheck
@ifGNUmake@ CPPCHECK_OPTS=--force --enable=all --quiet
@ifGNUmake@ ifeq ("$(C)", "2")
@ifGNUmake@ CHECK_CMD=$(CHECK) $(CHECK_OPTS) -Wbitwise -D__CHECK_ENDIAN__
@ifGNUmake@ CPPCHECK_CMD=$(CPPCHECK) $(CPPCHECK_OPTS)
@ifGNUmake@ else
@ifGNUmake@ ifeq ("$(C)", "1")
@ifGNUmake@ CHECK_CMD=$(CHECK) $(CHECK_OPTS)
@ifGNUmake@ CPPCHECK_CMD=$(CPPCHECK) $(CPPCHECK_OPTS)
@ifGNUmake@ else
@ifGNUmake@ CHECK_CMD=@true
@ifGNUmake@ CPPCHECK_CMD=@true
@ifGNUmake@ endif
@ifGNUmake@ endif
@ifNotGNUmake@ CHECK_CMD=true
@ifNotGNUmake@ CPPCHECK_CMD=true
SANITIZER_CFLAGS = @lto_cflags@ @ubsan_cflags@ @addrsan_cflags@ @threadsan_cflags@
SANITIZER_LDFLAGS = @lto_ldflags@ @ubsan_ldflags@ @addrsan_ldflags@ @threadsan_ldflags@
CC = @PTHREAD_CC@
BUILD_CC = @BUILD_CC@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LIBS = @PTHREAD_LIBS@
CFLAGS = @CFLAGS@
CFLAGS_SHLIB = @CFLAGS_SHLIB@
CFLAGS_STLIB = @CFLAGS_STLIB@
CPPFLAGS = @INCLUDES@
ALL_CFLAGS = $(CPPFLAGS) $(SANITIZER_CFLAGS) $(CFLAGS) $(PTHREAD_CFLAGS) $(CFLAGS_WARN) @DEFS@ $(LOCAL_CFLAGS)
ALL_CFLAGS_SHLIB = $(CPPFLAGS) $(SANITIZER_CFLAGS) $(CFLAGS_SHLIB) $(PTHREAD_CFLAGS) $(CFLAGS_WARN) @DEFS@ $(LOCAL_CFLAGS)
ALL_CFLAGS_STLIB = $(CPPFLAGS) $(SANITIZER_CFLAGS) $(CFLAGS_STLIB) $(PTHREAD_CFLAGS) $(CFLAGS_WARN) @DEFS@ $(LOCAL_CFLAGS)
LDFLAGS = $(SANITIZER_LDFLAGS) $(PTHREAD_CFLAGS) @LDFLAGS@
LDFLAGS_SHLIB = $(SANITIZER_LDFLAGS) $(PTHREAD_CFLAGS) @LDFLAGS_SHLIB@
ALL_LDFLAGS = $(LDFLAGS) @LDFLAG_DYNAMIC@
LDFLAGS_STATIC = $(SANITIZER_LDFLAGS) $(PTHREAD_CFLAGS) @LDFLAGS_STATIC@
BUILD_CFLAGS = $(SANITIZER_CFLAGS) @BUILD_CFLAGS@
BUILD_LDFLAGS = $(SANITIZER_LDFLAGS) @BUILD_LDFLAGS@
RDYNAMIC = @RDYNAMIC@
LINK_BUILD_FLAGS = @LINK_BUILD_FLAGS@
LINK_INSTALL_FLAGS = @LINK_INSTALL_FLAGS@
RM = @RM@
LN = @LN@
LN_S = @LN_S@
MV = @MV@
CP = @CP@
CHMOD = @CHMOD@
AR = @AR@
AWK = @AWK@
SED = @SED@
PERL = @PERL@
RANLIB = @RANLIB@
STRIP = @STRIP@
LD = $(PURE) @CC@
ARUPD = $(AR) r
ARGEN = $(AR) rc
LDCONFIG = @LDCONFIG@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
#
# Library definitions
#
LIB = $(top_builddir)/lib
LIBSS = $(LIB)/libss@LIB_EXT@ @PRIVATE_LIBS_CMT@ @DLOPEN_LIB@
LIBCOM_ERR = $(LIB)/libcom_err@LIB_EXT@ @PRIVATE_LIBS_CMT@ @SEM_INIT_LIB@
LIBE2P = $(LIB)/libe2p@LIB_EXT@
LIBEXT2FS = $(LIB)/libext2fs@LIB_EXT@
LIBUUID = @LIBUUID@ @SOCKET_LIB@
LIBMAGIC = @MAGIC_LIB@
LIBFUSE = @FUSE_LIB@
LIBSUPPORT = $(LIBINTL) $(LIB)/libsupport@STATIC_LIB_EXT@
LIBBLKID = @LIBBLKID@ @PRIVATE_LIBS_CMT@ $(LIBUUID)
LIBINTL = @LIBINTL@
SYSLIBS = @LIBS@ @PTHREAD_LIBS@
DEPLIBSS = $(LIB)/libss@LIB_EXT@
DEPLIBCOM_ERR = $(LIB)/libcom_err@LIB_EXT@
DEPLIBUUID = @DEPLIBUUID@
DEPLIBSUPPORT = $(LIB)/libsupport@STATIC_LIB_EXT@
DEPLIBBLKID = @DEPLIBBLKID@ @PRIVATE_LIBS_CMT@ $(DEPLIBUUID)
TESTENV = LD_LIBRARY_PATH="$(LIB):$${LD_LIBRARY_PATH}" DYLD_LIBRARY_PATH="$(LIB):$${DYLD_LIBRARY_PATH}"
STATIC_LIBSS = $(LIB)/libss@STATIC_LIB_EXT@ @DLOPEN_LIB@
STATIC_LIBCOM_ERR = $(LIB)/libcom_err@STATIC_LIB_EXT@ @SEM_INIT_LIB@
STATIC_LIBE2P = $(LIB)/libe2p@STATIC_LIB_EXT@
STATIC_LIBEXT2FS = $(LIB)/libext2fs@STATIC_LIB_EXT@
STATIC_LIBUUID = @STATIC_LIBUUID@ @SOCKET_LIB@
STATIC_LIBSUPPORT = $(LIBINTL) $(LIBSUPPORT)
STATIC_LIBBLKID = @STATIC_LIBBLKID@ $(STATIC_LIBUUID)
DEPSTATIC_LIBSS = $(LIB)/libss@STATIC_LIB_EXT@
DEPSTATIC_LIBCOM_ERR = $(LIB)/libcom_err@STATIC_LIB_EXT@
DEPSTATIC_LIBUUID = @DEPSTATIC_LIBUUID@
DEPSTATIC_LIBSUPPORT = $(DEPLIBSUPPORT)
DEPSTATIC_LIBBLKID = @DEPSTATIC_LIBBLKID@ $(DEPSTATIC_LIBUUID)
PROFILED_LIBSS = $(LIB)/libss@PROFILED_LIB_EXT@ @DLOPEN_LIB@
PROFILED_LIBCOM_ERR = $(LIB)/libcom_err@PROFILED_LIB_EXT@ @SEM_INIT_LIB@
PROFILED_LIBE2P = $(LIB)/libe2p@PROFILED_LIB_EXT@
PROFILED_LIBEXT2FS = $(LIB)/libext2fs@PROFILED_LIB_EXT@
PROFILED_LIBUUID = @PROFILED_LIBUUID@ @SOCKET_LIB@
PROFILED_LIBSUPPORT = $(LIBINTL) $(LIB)/libsupport@PROFILED_LIB_EXT@
PROFILED_LIBBLKID = @PROFILED_LIBBLKID@ $(PROFILED_LIBUUID)
DEPPROFILED_LIBSS = $(LIB)/libss@PROFILED_LIB_EXT@
DEPPROFILED_LIBCOM_ERR = $(LIB)/libcom_err@PROFILED_LIB_EXT@
DEPPROFILED_LIBUUID = @PROFILED_LIBUUID@
DEPPROFILED_LIBSUPPORT = $(PROFILED_LIBSUPPORT)
DEPPROFILED_LIBBLKID = @PROFILED_LIBBLKID@ $(DEPPROFILED_LIBUUID)
#
# A fast substitution command for fixing up man pages, shell scripts, etc.
#
SUBST_CONF=$(top_builddir)/util/subst.conf
SUBSTITUTE= $(top_builddir)/util/subst -f $(SUBST_CONF)
SUBSTITUTE_UPTIME= $(top_builddir)/util/subst -t -f $(SUBST_CONF)
DEP_SUBSTITUTE= $(top_builddir)/util/subst $(SUBST_CONF)
$(top_builddir)/util/subst:
cd $(top_builddir)/util ; $(MAKE) subst
#
# Script for generating utf8data.h
#
MKUTF8DATA=$(top_builddir)/util/mkutf8data
$(top_builddir)/util/mkutf8data:
$(MAKE) -C $(top_builddir)/util mkutf8data
#
# Script for installing symlinks (for shared libraries)
#
$(top_builddir)/util/install-symlink: $(top_srcdir)/util/install-symlink.in \
$(top_builddir)/config.status
cd $(top_builddir); CONFIG_FILES=util/install-symlink ./config.status
chmod +x $(top_builddir)/util/install-symlink
$(top_builddir)/util/symlinks:
cd $(top_builddir)/util ; $(MAKE) symlinks
INSTALL_SYMLINK = /bin/sh $(top_builddir)/util/install-symlink \
@SYMLINK_RELATIVE@ \
--symlinks=$(top_builddir)/util/symlinks
DEP_INSTALL_SYMLINK = $(top_builddir)/util/install-symlink \
$(top_builddir)/util/symlinks
#
# Warning flags
#
# Run make gcc-wall to do a build with warning messages.
#
#
WFLAGS= -std=gnu99 -D_XOPEN_SOURCE=600 -D_GNU_SOURCE $(WFLAGS_EXTRA) \
-Wall -W -Wwrite-strings -Wpointer-arith \
-Wcast-qual -Wcast-align -Wno-variadic-macros \
-Wstrict-prototypes -Wmissing-prototypes \
-Wformat-security -Wformat-nonliteral \
-Wmissing-format-attribute -O2 -Wstrict-aliasing \
-Wnested-externs -Winline -DNO_INLINE_FUNCS -Wshadow \
-UENABLE_NLS
gcc-wall-new:
($(MAKE) CFLAGS_WARN="$(WFLAGS)" > /dev/null) 2>&1
gcc-wall:
$(MAKE) clean > /dev/null
$(MAKE) gcc-wall-new
static-check:
($(MAKE) C=1 V=1 CFLAGS="$(ALL_CFLAGS) $(WFLAGS)") 2>&1
static-check-all:
$(MAKE) clean > /dev/null
$(MAKE) static-check
#
# Installation user and groups
#
BINGRP= bin
BINOWN= bin
BINMODE= 555
INCGRP= bin
INCOWN= bin
INCMODE= 444
LIBOWN= bin
LIBGRP= bin
LIBMODE= 444
MANGRP= bin
MANOWN= bin
MANMODE= 444
#
# Autoconf magic...
#
DEP_LIB_MAKEFILES = $(top_srcdir)/lib/Makefile.library \
$(top_srcdir)/lib/Makefile.elf-lib \
$(top_srcdir)/lib/Makefile.bsd-lib \
$(top_srcdir)/lib/Makefile.darwin-lib \
$(top_srcdir)/lib/Makefile.solaris-lib \
$(top_srcdir)/lib/Makefile.profile
$(top_builddir)/config.status: $(top_srcdir)/configure
cd $(top_builddir); ./config.status --recheck
$(top_builddir)/MCONFIG: $(top_srcdir)/MCONFIG.in $(top_builddir)/config.status
cd $(top_builddir); CONFIG_FILES=MCONFIG ./config.status
$(top_builddir)/lib/config.h: $(top_srcdir)/lib/config.h.in \
$(top_builddir)/config.status
cd $(top_builddir); CONFIG_FILES=lib/config.h ./config.status
$(top_builddir)/lib/dirpaths.h: $(DEP_SUBSTITUTE) $(top_srcdir)/lib/dirpaths.h.in
$(E) " SUBST $@"
$(Q) $(SUBSTITUTE) $(top_srcdir)/lib/dirpaths.h.in $@
$(top_builddir)/lib/substitute_sh: $(top_srcdir)/lib/substitute_sh.in \
$(top_builddir)/config.status
cd $(top_builddir); CONFIG_FILES=lib/substitute_sh ./config.status
$(top_builddir)/util/subst.conf: $(top_srcdir)/util/subst.conf.in \
$(top_builddir)/config.status
cd $(top_builddir); CONFIG_FILES=util/subst.conf ./config.status
Makefile: $(srcdir)/Makefile.in $(top_builddir)/MCONFIG \
$(DEP_MAKEFILE) $(top_builddir)/config.status
cd $(top_builddir); CONFIG_FILES=$(my_dir)/Makefile ./config.status
@MAINTAINER_CMT@$(top_srcdir)/configure: $(top_srcdir)/configure.ac
@MAINTAINER_CMT@ cd $(top_srcdir) && autoheader && autoconf
coverage.txt: Makefile $(SRCS)
if test -n "$(SRCS)"; then \
gcov -s $(top_srcdir) -o . $(SRCS) > coverage.txt 2>&1 ; \
fi
clean::
$(RM) -f *.gcda *.gcov *.gcno coverage.txt
#
# Make depend magic...
#
.depend: Makefile $(SRCS) $(top_srcdir)/depfix.sed $(top_srcdir)/wordwrap.pl
if test -n "$(SRCS)" ; then \
$(CC) -M $(ALL_CFLAGS) $(DEPEND_CFLAGS) $(SRCS) | \
$(SED) -f $(top_srcdir)/depfix.sed \
-e 's; $(srcdir)/; $$(srcdir)/;g' \
-e 's; $(top_srcdir)/; $$(top_srcdir)/;g' \
-e 's; $(top_builddir)/; $$(top_builddir)/;g' \
-e 's; \./; ;g' \
-e '/^#/d' \
-e '/^ *\\$$/d' | \
$(PERL) $(top_srcdir)/wordwrap.pl > .depend; \
else :; fi
depend:: .depend
if test -n "$(SRCS)" ; then \
sed -e '/^# +++ Dependency line eater +++/,$$d' \
< $(srcdir)/Makefile.in | cat - .depend \
> $(srcdir)/Makefile.in.new; \
if cmp -s $(srcdir)/Makefile.in $(srcdir)/Makefile.in.new ; then \
$(RM) $(srcdir)/Makefile.in.new ; \
else \
$(MV) $(srcdir)/Makefile.in $(srcdir)/Makefile.in.old; \
$(MV) $(srcdir)/Makefile.in.new $(srcdir)/Makefile.in; \
fi ; else :; fi
# End of file MCONFIG

View file

@ -0,0 +1,170 @@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
top_builddir = .
my_dir = .
INSTALL = @INSTALL@
MKDIR_P = @MKDIR_P@
@MCONFIG@
% : %.sh
@RESIZER_CMT@RESIZE_DIR= resize
@DEBUGFS_CMT@DEBUGFS_DIR= debugfs
@UUID_CMT@UUID_LIB_SUBDIR= lib/uuid
@BLKID_CMT@BLKID_LIB_SUBDIR= lib/blkid
@E2SCRUB_CMT@E2SCRUB_DIR= scrub
@ALL_CMT@SUPPORT_LIB_SUBDIR= lib/support
@ALL_CMT@E2P_LIB_SUBDIR= lib/e2p
@ALL_CMT@EXT2FS_LIB_SUBDIR= lib/ext2fs
LIB_SUBDIRS=lib/et lib/ss $(E2P_LIB_SUBDIR) $(UUID_LIB_SUBDIR) \
$(BLKID_LIB_SUBDIR) $(SUPPORT_LIB_SUBDIR) $(EXT2FS_LIB_SUBDIR)
PROG_SUBDIRS=e2fsck $(DEBUGFS_DIR) misc $(RESIZE_DIR) tests/progs po \
$(E2SCRUB_DIR)
SUBDIRS=util $(LIB_SUBDIRS) $(PROG_SUBDIRS) tests
SUBS= util/subst.conf lib/config.h $(top_builddir)/lib/dirpaths.h \
lib/ext2fs/ext2_types.h lib/blkid/blkid_types.h lib/uuid/uuid_types.h
TAR=tar
all:: subs
$(MAKE) libs
@ALL_CMT@ $(MAKE) progs
@ALL_CMT@ $(MAKE) docs
all-static::
$(MAKE) libs
@ALL_CMT@ $(MAKE) static-progs
subs: $(DEP_SUBSTITUTE)
@for i in $(SUBS) ; do if test -d `dirname $$i` ; \
then $(MAKE) $$i || exit $$? ; fi ; done
@(if test -d lib/et ; then cd lib/et && $(MAKE) compile_et; fi)
@(if test -d lib/ext2fs ; then cd lib/ext2fs && $(MAKE) ext2_err.h; fi)
@(if test -d lib/support ; then cd lib/support && $(MAKE) prof_err.h; fi)
progs: all-progs-recursive
static-progs: all-static-progs-recursive
libs: all-libs-recursive
all-progs-recursive all-libs-recursive:: subs
rpm:
sh contrib/build-rpm
docs:
-@test -d doc && cd doc && $(MAKE) libext2fs.info
install-doc-libs:
-@test -d doc && cd doc && $(MAKE) install-doc-libs
uninstall-doc-libs:
-@test -d doc && cd doc && $(MAKE) uninstall-doc-libs
clean-doc:
-@test -d doc && cd doc && $(MAKE) clean
distclean-doc:
-test -d doc && cd doc && $(MAKE) distclean
install: subs all-libs-recursive install-progs-recursive \
install-shlibs-libs-recursive install-doc-libs
@SUBSET_CMT@ $(MAKE) install-libs
install-strip: subs all-libs-recursive install-strip-progs-recursive \
install-shlibs-strip-libs-recursive install-doc-libs
uninstall: uninstall-progs-recursive uninstall-shlibs-libs-recursive uninstall-doc-libs
install-libs: install-libs-recursive
uninstall-libs: uninstall-libs-recursive
coverage.txt: coverage.txt-recursive
check-recursive: all
TAGS clean-recursive distclean-recursive depend-recursive fullcheck-recursive \
check-recursive mostlyclean-recursive realclean-recursive \
coverage.txt-recursive:
@for subdir in $(SUBDIRS); do \
if test -d $$subdir ; then \
target=`echo $@|$(SED) 's/-recursive//'`; \
echo making $$target in $$subdir; \
(cd $$subdir && $(MAKE) $$target) || exit 1; \
fi ; \
done
all-progs-recursive install-progs-recursive install-strip-progs-recursive \
uninstall-progs-recursive coverage.txt-progs-recursive:: all-libs-recursive
@ALL_CMT@all-progs-recursive all-static-progs-recursive install-progs-recursive \
@ALL_CMT@ install-strip-progs-recursive uninstall-progs-recursive \
@ALL_CMT@ coverage.txt-progs-recursive:: all-libs-recursive
@ALL_CMT@ @for subdir in $(PROG_SUBDIRS); do \
@ALL_CMT@ if test -d $$subdir ; then \
@ALL_CMT@ target=`echo $@|$(SED) 's/-progs-recursive//'`; \
@ALL_CMT@ echo making $$target in $$subdir; \
@ALL_CMT@ (cd $$subdir && $(MAKE) $$target) || exit 1; \
@ALL_CMT@ fi ; \
@ALL_CMT@ done
all-libs-recursive install-libs-recursive install-strip-libs-recursive \
uninstall-libs-recursive install-shlibs-libs-recursive \
install-shlibs-strip-libs-recursive uninstall-shlibs-libs-recursive \
coverage.txt-libs-recursive::
@for subdir in $(LIB_SUBDIRS); do \
if test -d $$subdir ; then \
target=`echo $@|$(SED) 's/-libs-recursive//'`; \
echo making $$target in $$subdir; \
(cd $$subdir && $(MAKE) $$target) || exit 1; \
fi ; \
done
mostlyclean: mostlyclean-recursive mostlyclean-local
clean:: clean-recursive clean-local clean-doc
$(RM) -f $(SUBS)
distclean: distclean-doc distclean-recursive
$(RM) -rf autom4te.cache ext2ed/Makefile po/stamp-po \
asm_types.h config.log public_config.h parse-types.log
$(MAKE) distclean-local
realclean: realclean-recursive realclean-local
depend:: depend-recursive
lib/ext2fs/ext2_types.h: $(DEP_SUBSTITUTE) asm_types.h \
$(srcdir)/lib/ext2fs/ext2_types.h.in
cd $(top_builddir); CONFIG_FILES=./lib/ext2fs/ext2_types.h ./config.status
lib/blkid/blkid_types.h: $(DEP_SUBSTITUTE) asm_types.h \
$(srcdir)/lib/blkid/blkid_types.h.in
cd $(top_builddir); CONFIG_FILES=./lib/blkid/blkid_types.h ./config.status
lib/uuid/uuid_types.h: $(DEP_SUBSTITUTE) asm_types.h \
$(srcdir)/lib/uuid/uuid_types.h.in
cd $(top_builddir); CONFIG_FILES=./lib/uuid/uuid_types.h ./config.status
mostlyclean-local:
$(RM) -f \#* *~ *.orig core MAKELOG
clean-local: mostlyclean-local
distclean-local: clean-local
$(RM) -f $(SUBS) $(SUBST_CONF) \
config.status config.log config.cache MCONFIG Makefile \
$(srcdir)/TAGS $(srcdir)/Makefile.in.old
realclean-local: distclean-local
$(RM) -f configure
check:: all check-recursive
fullcheck:: all fullcheck-recursive

View file

@ -0,0 +1,849 @@
This package, the EXT2 filesystem utilities, are made available under
the GNU Public License version 2, with the exception of the lib/ext2fs
and lib/e2p libraries, which are made available under the GNU Library
General Public License Version 2, the lib/uuid library which is made
available under a BSD-style license and the lib/et and lib/ss
libraries which are made available under an MIT-style license. Please
see lib/uuid/COPYING for more details for the license for the files
comprising the libuuid library, and the source file headers of the
libet and libss libraries for more information.
The most recent officially distributed version can be found at
http://e2fsprogs.sourceforge.net. If you need to make a distribution,
that's the one you should use. If there is some reason why you'd like
a more recent version that is still in ALPHA testing (i.e., either
using the "WIP" test distributions or one from the hg or git
repository from the development branch, please contact me
(tytso@mit.edu) before you ship. The release schedules for this
package are flexible, if you give me enough lead time.
Theodore Ts'o
23-June-2007
----------------------------------------------------------------------
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
----------------------------------------------------------------------
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for
your libraries, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the library, or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on
the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
Most GNU software, including some libraries, is covered by the ordinary
GNU General Public License, which was designed for utility programs. This
license, the GNU Library General Public License, applies to certain
designated libraries. This license is quite different from the ordinary
one; be sure to read it in full, and don't assume that anything in it is
the same as in the ordinary license.
The reason we have a separate public license for some libraries is that
they blur the distinction we usually make between modifying or adding to a
program and simply using it. Linking a program with a library, without
changing the library, is in some sense simply using the library, and is
analogous to running a utility program or application program. However, in
a textual and legal sense, the linked executable is a combined work, a
derivative of the original library, and the ordinary General Public License
treats it as such.
Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended to
permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to achieve
this as regards changes in header files, but we have achieved it as regards
changes in the actual functions of the Library.) The hope is that this
will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only
works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other authorized
party saying it may be distributed under the terms of this Library
General Public License (also called "this License"). Each licensee is
addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
c) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
d) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the source code distributed need not include anything that is normally
distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Library General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library 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
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View file

@ -0,0 +1,19 @@
This is the new version (1.46.2) of the second extended file
system management programs.
From time to time, I release new versions of e2fsprogs, to fix
bugs and to make the utilities more robust. You can always find
information about the latest version at the the e2fsprogs web page,
which is:
http://e2fsprogs.sourceforge.net
The INSTALL file has instructions on building and installing
e2fsprogs. Provisions for building Red Hat RPMs and Debian dpkg files
are supplied as well.
In case of bugs in these programs, please contact Ted Ts'o at
tytso@mit.edu or tytso@alum.mit.edu. See the e2fsck man page for
suggestions of what sort of information to include when submitting bug
reports for these programs.

View file

@ -0,0 +1,64 @@
E2fsprogs 1.46.2 (February 28, 2021)
===================================
Updates/Fixes since v1.46.1:
UI and Features
---------------
Teach the tune2fs program to support "random" as an argument to the -c
option, which sets the maximum mount count. (Addresses Debian Bug:
#926293)
Add support for the FS_NOCOMP_FL flag to chattr and lsattr.
Fixes
-----
When resizing a small file systems to a super-large file system size,
avoid issuing some scary bitmap operation warnings. (Addresses Github
issue https://github.com/tytso/e2fsprogs/issues/60)
Fix the debugfs rdump and ls commands so they will work correctly for
uid's and gid's => 65536. (Addresses Github issue issue
https://github.com/tytso/e2fsprogs/issues/63)
Fix the debugfs write and symlink commands so they support targets which
contain a pathname (instead of only working when writing a file or
creating a symlink in the current directory). (Addresses Github issue
https://github.com/tytso/e2fsprogs/issues/61)
Fix Direct I/O support on block devices where the logical block size is
greater 1k. (This includes Advanced Format HDD's, where the sector size
is 4k, and IBM Mainframe DASD's, where the sector size is 2k.)
Fix debugfs's logdump so it works on file systems whose block size is
greater than 8k.
Fix a where e2fsck could a crash when there is error while e2fsck is
trying to open the file system, and e2fsck calls ext2fs_mmp_stop()
before MMP has been initialized. (Addresses Debian Bug: #696609)
Improved error checking in the fast commit replay code in e2fsck.
Updated and clarified the chattr man page.
Performance, Internal Implementation, Development Support etc.
--------------------------------------------------------------
Fix various compiler and Coverity warnings.
Update the Spanish translation from the translation project.
Update the e2fsck/iscan.c test program so that it builds again.
Fix an environmental dependency bug for the m_rootdir_acl regression
test.
Avoid the use of loff_t, which is not available for all compilers /
system include files.
Fix failure of the t_mmp_fail test when running on a device with a 4k
logical sector size.

View file

@ -0,0 +1,35 @@
Library:libcom_err.o
Description: Common error code library
Maintainer: Theodore Ts'o
Email: tytso@mit.edu
Start: 0x66800000
End: 0x6687ffff
Library:libss.o
Description: Generic Subsystem library (Simple tty UI)
Maintainer: Theodore Ts'o
Email: tytso@mit.edu
Start: 0x66880000
End: 0x668fffff
Library:libext2fs.so
Description: The ext2fs (raw interface) library
Maintainer: Theodore Ts'o
Email: tytso@mit.edu
Start: 0x66900000
End: 0x6697ffff
Library:libe2p.so
Description: The e2p (ext2fs's programmers) library
Maintainer: Theodore Ts'o
Email: tytso@mit.edu
Start: 0x66980000
End: 0x669fffff
Library:libuuid.so
Description: DCE Universally Unique ID (UUID) library
Maintainer: Theodore Ts'o
Email: tytso@mit.edu
Start: 0x67900000
End: 0x679fffff

View file

@ -0,0 +1,39 @@
Like the Linux kernel, submitted e2fsprogs patches now require the
following "sign-off" procedure:
The sign-off is a simple line at the end of the explanation for the
patch, which certifies that you wrote it or otherwise have the right to
pass it on as a open-source patch. The rules are pretty simple: if you
can certify the below:
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
then you just add a line saying
Signed-off-by: Random J Developer <random@developer.example.org>

View file

@ -0,0 +1,178 @@
# from http://autoconf-archive.cryp.to/ax_tls.html
#
# This was licensed under the GPL with the following exception:
#
# As a special exception, the respective Autoconf Macro's copyright
# owner gives unlimited permission to copy, distribute and modify the
# configure scripts that are the output of Autoconf when processing
# the Macro. You need not follow the terms of the GNU General Public
# License when using or distributing such scripts, even though
# portions of the text of the Macro appear in them. The GNU General
# Public License (GPL) does govern all other use of the material that
# constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the
# Autoconf Macro released by the Autoconf Macro Archive. When you make
# and distribute a modified version of the Autoconf Macro, you may
# extend this special exception to the GPL to apply to your modified
# version as well.
#
AC_DEFUN([AX_TLS], [
AC_MSG_CHECKING(for thread local storage (TLS) class)
AC_CACHE_VAL(ac_cv_tls, [
ax_tls_keywords="__thread __declspec(thread) none"
for ax_tls_keyword in $ax_tls_keywords; do
case $ax_tls_keyword in
none) ac_cv_tls=none ; break ;;
*)
AC_TRY_COMPILE(
[#include <stdlib.h>
static void
foo(void) {
static ] $ax_tls_keyword [ int bar;
exit(1);
}],
[],
[ac_cv_tls=$ax_tls_keyword ; break],
ac_cv_tls=none
)
esac
done
])
if test "$ac_cv_tls" != "none"; then
dnl AC_DEFINE([TLS], [], [If the compiler supports a TLS storage class define it to that here])
AC_DEFINE_UNQUOTED([TLS], $ac_cv_tls, [If the compiler supports a TLS storage class define it to that here])
fi
AC_MSG_RESULT($ac_cv_tls)
])
# ===========================================================================
# http://www.nongnu.org/autoconf-archive/check_gnu_make.html
# ===========================================================================
#
# SYNOPSIS
#
# CHECK_GNU_MAKE()
#
# DESCRIPTION
#
# This macro searches for a GNU version of make. If a match is found, the
# makefile variable `ifGNUmake' is set to the empty string, otherwise it
# is set to "#". This is useful for including a special features in a
# Makefile, which cannot be handled by other versions of make. The
# variable _cv_gnu_make_command is set to the command to invoke GNU make
# if it exists, the empty string otherwise.
#
# Here is an example of its use:
#
# Makefile.in might contain:
#
# # A failsafe way of putting a dependency rule into a makefile
# $(DEPEND):
# $(CC) -MM $(srcdir)/*.c > $(DEPEND)
#
# @ifGNUmake@ ifeq ($(DEPEND),$(wildcard $(DEPEND)))
# @ifGNUmake@ include $(DEPEND)
# @ifGNUmake@ endif
#
# Then configure.in would normally contain:
#
# CHECK_GNU_MAKE()
# AC_OUTPUT(Makefile)
#
# Then perhaps to cause gnu make to override any other make, we could do
# something like this (note that GNU make always looks for GNUmakefile
# first):
#
# if ! test x$_cv_gnu_make_command = x ; then
# mv Makefile GNUmakefile
# echo .DEFAULT: > Makefile ;
# echo \ $_cv_gnu_make_command \$@ >> Makefile;
# fi
#
# Then, if any (well almost any) other make is called, and GNU make also
# exists, then the other make wraps the GNU make.
#
# LICENSE
#
# Copyright (c) 2008 John Darrington <j.darrington@elvis.murdoch.edu.au>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved.
#
# Note: Modified by Ted Ts'o to add @ifNotGNUMake@
AC_DEFUN(
[CHECK_GNU_MAKE], [ AC_CACHE_CHECK( for GNU make,_cv_gnu_make_command,
_cv_gnu_make_command='' ;
dnl Search all the common names for GNU make
if test -n "$FORCE_NATIVE_MAKE" ; then
MAKES="make"
else
MAKES="make gmake gnumake"
fi
for a in "$MAKE" $MAKES ; do
if test -z "$a" ; then continue ; fi ;
if ( sh -c "$a --version" 2> /dev/null | grep GNU 2>&1 > /dev/null ) ; then
_cv_gnu_make_command=$a ;
break;
fi
done ;
) ;
dnl If there was a GNU version, then set @ifGNUmake@ to the empty string, '#' otherwise
if test "x$_cv_gnu_make_command" != "x" ; then
ifGNUmake='' ;
ifNotGNUmake='#' ;
else
ifGNUmake='#' ;
ifNotGNUmake='' ;
AC_MSG_RESULT("Not found");
fi
AC_SUBST(ifGNUmake)
AC_SUBST(ifNotGNUmake)
] )
# AX_CHECK_MOUNT_OPT: an autoconf macro to check for generic filesystem-
# agnostic 'mount' options. Written by Nicholas Clark. Looks for constants in
# sys/mount.h to predict whether the 'mount' utility will support a specific
# mounting option.
#
# This macro can be used to check for the presence of 'nodev' (or other mount
# options), which isn't uniformly implemented in the BSD family at the time of
# this writing. Tested on FreeBSD, NetBSD, OpenBSD, and Linux.
#
# Usage:
#
# AX_CHECK_MOUNT_OPT(option)
#
# Defines HAVE_MOUNT_$OPTION (in uppercase) if the option exists, and sets
# ac_cv_mount_$option (in original case) otherwise.
#
# Copyright (c) 2018 Nicholas Clark <nicholas.clark@gmail.com>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty or attribution requirement.
AC_DEFUN([AX_CHECK_MOUNT_OPT], [__AX_CHECK_MOUNT_OPT(m4_tolower([$1]),m4_toupper([$1]))])
AC_DEFUN([__AX_CHECK_MOUNT_OPT],
[
AS_ECHO_N("checking for mount '$1' option... ")
AC_TRY_COMPILE(
[#include <sys/mount.h>],
[void *temp = (void *)(MS_$2); (void) temp;],
[AC_DEFINE(HAVE_MOUNT_$2, 1, [Define to 1 if mount supports $1.])
AS_VAR_SET(ac_cv_mount_$1, yes)
AS_ECHO("yes")],
[AC_TRY_COMPILE(
[#include <sys/mount.h>],
[void *temp = (void *)(MNT_$2); (void) temp;],
[AC_DEFINE(HAVE_MOUNT_$2, 1, [Define to 1 if mount supports $1.])
AS_VAR_SET(ac_cv_mount_$1, yes)
AS_ECHO("yes")],
[AS_VAR_SET(ac_cv_mount_$1, no)
AS_ECHO("no")]
)]
)
])

4062
2025-03-19/e2fsprogs-1.46.2/aclocal.m4 vendored Normal file

File diff suppressed because it is too large Load diff

1667
2025-03-19/e2fsprogs-1.46.2/config/config.guess vendored Executable file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,684 @@
#! /bin/sh
# Output a system dependent set of variables, describing how to set the
# run time search path of shared libraries in an executable.
#
# Copyright 1996-2016 Free Software Foundation, Inc.
# Taken from GNU libtool, 2001
# Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# The first argument passed to this file is the canonical host specification,
# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
# or
# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
# The environment variables CC, GCC, LDFLAGS, LD, with_gnu_ld
# should be set by the caller.
#
# The set of defined variables is at the end of this script.
# Known limitations:
# - On IRIX 6.5 with CC="cc", the run time search patch must not be longer
# than 256 bytes, otherwise the compiler driver will dump core. The only
# known workaround is to choose shorter directory names for the build
# directory and/or the installation directory.
# All known linkers require a '.a' archive for static linking (except MSVC,
# which needs '.lib').
libext=a
shrext=.so
host="$1"
host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
# Code taken from libtool.m4's _LT_CC_BASENAME.
for cc_temp in $CC""; do
case $cc_temp in
compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
\-*) ;;
*) break;;
esac
done
cc_basename=`echo "$cc_temp" | sed -e 's%^.*/%%'`
# Code taken from libtool.m4's _LT_COMPILER_PIC.
wl=
if test "$GCC" = yes; then
wl='-Wl,'
else
case "$host_os" in
aix*)
wl='-Wl,'
;;
mingw* | cygwin* | pw32* | os2* | cegcc*)
;;
hpux9* | hpux10* | hpux11*)
wl='-Wl,'
;;
irix5* | irix6* | nonstopux*)
wl='-Wl,'
;;
linux* | k*bsd*-gnu | kopensolaris*-gnu)
case $cc_basename in
ecc*)
wl='-Wl,'
;;
icc* | ifort*)
wl='-Wl,'
;;
lf95*)
wl='-Wl,'
;;
nagfor*)
wl='-Wl,-Wl,,'
;;
pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)
wl='-Wl,'
;;
ccc*)
wl='-Wl,'
;;
xl* | bgxl* | bgf* | mpixl*)
wl='-Wl,'
;;
como)
wl='-lopt='
;;
*)
case `$CC -V 2>&1 | sed 5q` in
*Sun\ F* | *Sun*Fortran*)
wl=
;;
*Sun\ C*)
wl='-Wl,'
;;
esac
;;
esac
;;
newsos6)
;;
*nto* | *qnx*)
;;
osf3* | osf4* | osf5*)
wl='-Wl,'
;;
rdos*)
;;
solaris*)
case $cc_basename in
f77* | f90* | f95* | sunf77* | sunf90* | sunf95*)
wl='-Qoption ld '
;;
*)
wl='-Wl,'
;;
esac
;;
sunos4*)
wl='-Qoption ld '
;;
sysv4 | sysv4.2uw2* | sysv4.3*)
wl='-Wl,'
;;
sysv4*MP*)
;;
sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
wl='-Wl,'
;;
unicos*)
wl='-Wl,'
;;
uts4*)
;;
esac
fi
# Code taken from libtool.m4's _LT_LINKER_SHLIBS.
hardcode_libdir_flag_spec=
hardcode_libdir_separator=
hardcode_direct=no
hardcode_minus_L=no
case "$host_os" in
cygwin* | mingw* | pw32* | cegcc*)
# FIXME: the MSVC++ port hasn't been tested in a loooong time
# When not using gcc, we currently assume that we are using
# Microsoft Visual C++.
if test "$GCC" != yes; then
with_gnu_ld=no
fi
;;
interix*)
# we just hope/assume this is gcc and not c89 (= MSVC++)
with_gnu_ld=yes
;;
openbsd*)
with_gnu_ld=no
;;
esac
ld_shlibs=yes
if test "$with_gnu_ld" = yes; then
# Set some defaults for GNU ld with shared library support. These
# are reset later if shared libraries are not supported. Putting them
# here allows them to be overridden if necessary.
# Unlike libtool, we use -rpath here, not --rpath, since the documented
# option of GNU ld is called -rpath, not --rpath.
hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
case "$host_os" in
aix[3-9]*)
# On AIX/PPC, the GNU linker is very broken
if test "$host_cpu" != ia64; then
ld_shlibs=no
fi
;;
amigaos*)
case "$host_cpu" in
powerpc)
;;
m68k)
hardcode_libdir_flag_spec='-L$libdir'
hardcode_minus_L=yes
;;
esac
;;
beos*)
if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
:
else
ld_shlibs=no
fi
;;
cygwin* | mingw* | pw32* | cegcc*)
# hardcode_libdir_flag_spec is actually meaningless, as there is
# no search path for DLLs.
hardcode_libdir_flag_spec='-L$libdir'
if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then
:
else
ld_shlibs=no
fi
;;
haiku*)
;;
interix[3-9]*)
hardcode_direct=no
hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
;;
gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
:
else
ld_shlibs=no
fi
;;
netbsd*)
;;
solaris*)
if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then
ld_shlibs=no
elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
:
else
ld_shlibs=no
fi
;;
sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
case `$LD -v 2>&1` in
*\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
ld_shlibs=no
;;
*)
if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`'
else
ld_shlibs=no
fi
;;
esac
;;
sunos4*)
hardcode_direct=yes
;;
*)
if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
:
else
ld_shlibs=no
fi
;;
esac
if test "$ld_shlibs" = no; then
hardcode_libdir_flag_spec=
fi
else
case "$host_os" in
aix3*)
# Note: this linker hardcodes the directories in LIBPATH if there
# are no directories specified by -L.
hardcode_minus_L=yes
if test "$GCC" = yes; then
# Neither direct hardcoding nor static linking is supported with a
# broken collect2.
hardcode_direct=unsupported
fi
;;
aix[4-9]*)
if test "$host_cpu" = ia64; then
# On IA64, the linker does run time linking by default, so we don't
# have to do anything special.
aix_use_runtimelinking=no
else
aix_use_runtimelinking=no
# Test if we are trying to use run time linking or normal
# AIX style linking. If -brtl is somewhere in LDFLAGS, we
# need to do runtime linking.
case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
for ld_flag in $LDFLAGS; do
if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
aix_use_runtimelinking=yes
break
fi
done
;;
esac
fi
hardcode_direct=yes
hardcode_libdir_separator=':'
if test "$GCC" = yes; then
case $host_os in aix4.[012]|aix4.[012].*)
collect2name=`${CC} -print-prog-name=collect2`
if test -f "$collect2name" && \
strings "$collect2name" | grep resolve_lib_name >/dev/null
then
# We have reworked collect2
:
else
# We have old collect2
hardcode_direct=unsupported
hardcode_minus_L=yes
hardcode_libdir_flag_spec='-L$libdir'
hardcode_libdir_separator=
fi
;;
esac
fi
# Begin _LT_AC_SYS_LIBPATH_AIX.
echo 'int main () { return 0; }' > conftest.c
${CC} ${LDFLAGS} conftest.c -o conftest
aix_libpath=`dump -H conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; }
}'`
if test -z "$aix_libpath"; then
aix_libpath=`dump -HX64 conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; }
}'`
fi
if test -z "$aix_libpath"; then
aix_libpath="/usr/lib:/lib"
fi
rm -f conftest.c conftest
# End _LT_AC_SYS_LIBPATH_AIX.
if test "$aix_use_runtimelinking" = yes; then
hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
else
if test "$host_cpu" = ia64; then
hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib'
else
hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
fi
fi
;;
amigaos*)
case "$host_cpu" in
powerpc)
;;
m68k)
hardcode_libdir_flag_spec='-L$libdir'
hardcode_minus_L=yes
;;
esac
;;
bsdi[45]*)
;;
cygwin* | mingw* | pw32* | cegcc*)
# When not using gcc, we currently assume that we are using
# Microsoft Visual C++.
# hardcode_libdir_flag_spec is actually meaningless, as there is
# no search path for DLLs.
hardcode_libdir_flag_spec=' '
libext=lib
;;
darwin* | rhapsody*)
hardcode_direct=no
if { case $cc_basename in ifort*) true;; *) test "$GCC" = yes;; esac; }; then
:
else
ld_shlibs=no
fi
;;
dgux*)
hardcode_libdir_flag_spec='-L$libdir'
;;
freebsd2.[01]*)
hardcode_direct=yes
hardcode_minus_L=yes
;;
freebsd* | dragonfly*)
hardcode_libdir_flag_spec='-R$libdir'
hardcode_direct=yes
;;
hpux9*)
hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
hardcode_libdir_separator=:
hardcode_direct=yes
# hardcode_minus_L: Not really in the search PATH,
# but as the default location of the library.
hardcode_minus_L=yes
;;
hpux10*)
if test "$with_gnu_ld" = no; then
hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
hardcode_libdir_separator=:
hardcode_direct=yes
# hardcode_minus_L: Not really in the search PATH,
# but as the default location of the library.
hardcode_minus_L=yes
fi
;;
hpux11*)
if test "$with_gnu_ld" = no; then
hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
hardcode_libdir_separator=:
case $host_cpu in
hppa*64*|ia64*)
hardcode_direct=no
;;
*)
hardcode_direct=yes
# hardcode_minus_L: Not really in the search PATH,
# but as the default location of the library.
hardcode_minus_L=yes
;;
esac
fi
;;
irix5* | irix6* | nonstopux*)
hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
hardcode_libdir_separator=:
;;
netbsd*)
hardcode_libdir_flag_spec='-R$libdir'
hardcode_direct=yes
;;
newsos6)
hardcode_direct=yes
hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
hardcode_libdir_separator=:
;;
*nto* | *qnx*)
;;
openbsd*)
if test -f /usr/libexec/ld.so; then
hardcode_direct=yes
if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
else
case "$host_os" in
openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
hardcode_libdir_flag_spec='-R$libdir'
;;
*)
hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
;;
esac
fi
else
ld_shlibs=no
fi
;;
os2*)
hardcode_libdir_flag_spec='-L$libdir'
hardcode_minus_L=yes
;;
osf3*)
hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
hardcode_libdir_separator=:
;;
osf4* | osf5*)
if test "$GCC" = yes; then
hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
else
# Both cc and cxx compiler support -rpath directly
hardcode_libdir_flag_spec='-rpath $libdir'
fi
hardcode_libdir_separator=:
;;
solaris*)
hardcode_libdir_flag_spec='-R$libdir'
;;
sunos4*)
hardcode_libdir_flag_spec='-L$libdir'
hardcode_direct=yes
hardcode_minus_L=yes
;;
sysv4)
case $host_vendor in
sni)
hardcode_direct=yes # is this really true???
;;
siemens)
hardcode_direct=no
;;
motorola)
hardcode_direct=no #Motorola manual says yes, but my tests say they lie
;;
esac
;;
sysv4.3*)
;;
sysv4*MP*)
if test -d /usr/nec; then
ld_shlibs=yes
fi
;;
sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
;;
sysv5* | sco3.2v5* | sco5v6*)
hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`'
hardcode_libdir_separator=':'
;;
uts4*)
hardcode_libdir_flag_spec='-L$libdir'
;;
*)
ld_shlibs=no
;;
esac
fi
# Check dynamic linker characteristics
# Code taken from libtool.m4's _LT_SYS_DYNAMIC_LINKER.
# Unlike libtool.m4, here we don't care about _all_ names of the library, but
# only about the one the linker finds when passed -lNAME. This is the last
# element of library_names_spec in libtool.m4, or possibly two of them if the
# linker has special search rules.
library_names_spec= # the last element of library_names_spec in libtool.m4
libname_spec='lib$name'
case "$host_os" in
aix3*)
library_names_spec='$libname.a'
;;
aix[4-9]*)
library_names_spec='$libname$shrext'
;;
amigaos*)
case "$host_cpu" in
powerpc*)
library_names_spec='$libname$shrext' ;;
m68k)
library_names_spec='$libname.a' ;;
esac
;;
beos*)
library_names_spec='$libname$shrext'
;;
bsdi[45]*)
library_names_spec='$libname$shrext'
;;
cygwin* | mingw* | pw32* | cegcc*)
shrext=.dll
library_names_spec='$libname.dll.a $libname.lib'
;;
darwin* | rhapsody*)
shrext=.dylib
library_names_spec='$libname$shrext'
;;
dgux*)
library_names_spec='$libname$shrext'
;;
freebsd[23].*)
library_names_spec='$libname$shrext$versuffix'
;;
freebsd* | dragonfly*)
library_names_spec='$libname$shrext'
;;
gnu*)
library_names_spec='$libname$shrext'
;;
haiku*)
library_names_spec='$libname$shrext'
;;
hpux9* | hpux10* | hpux11*)
case $host_cpu in
ia64*)
shrext=.so
;;
hppa*64*)
shrext=.sl
;;
*)
shrext=.sl
;;
esac
library_names_spec='$libname$shrext'
;;
interix[3-9]*)
library_names_spec='$libname$shrext'
;;
irix5* | irix6* | nonstopux*)
library_names_spec='$libname$shrext'
case "$host_os" in
irix5* | nonstopux*)
libsuff= shlibsuff=
;;
*)
case $LD in
*-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= ;;
*-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 ;;
*-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 ;;
*) libsuff= shlibsuff= ;;
esac
;;
esac
;;
linux*oldld* | linux*aout* | linux*coff*)
;;
linux* | k*bsd*-gnu | kopensolaris*-gnu)
library_names_spec='$libname$shrext'
;;
knetbsd*-gnu)
library_names_spec='$libname$shrext'
;;
netbsd*)
library_names_spec='$libname$shrext'
;;
newsos6)
library_names_spec='$libname$shrext'
;;
*nto* | *qnx*)
library_names_spec='$libname$shrext'
;;
openbsd*)
library_names_spec='$libname$shrext$versuffix'
;;
os2*)
libname_spec='$name'
shrext=.dll
library_names_spec='$libname.a'
;;
osf3* | osf4* | osf5*)
library_names_spec='$libname$shrext'
;;
rdos*)
;;
solaris*)
library_names_spec='$libname$shrext'
;;
sunos4*)
library_names_spec='$libname$shrext$versuffix'
;;
sysv4 | sysv4.3*)
library_names_spec='$libname$shrext'
;;
sysv4*MP*)
library_names_spec='$libname$shrext'
;;
sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
library_names_spec='$libname$shrext'
;;
tpf*)
library_names_spec='$libname$shrext'
;;
uts4*)
library_names_spec='$libname$shrext'
;;
esac
sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
escaped_wl=`echo "X$wl" | sed -e 's/^X//' -e "$sed_quote_subst"`
shlibext=`echo "$shrext" | sed -e 's,^\.,,'`
escaped_libname_spec=`echo "X$libname_spec" | sed -e 's/^X//' -e "$sed_quote_subst"`
escaped_library_names_spec=`echo "X$library_names_spec" | sed -e 's/^X//' -e "$sed_quote_subst"`
escaped_hardcode_libdir_flag_spec=`echo "X$hardcode_libdir_flag_spec" | sed -e 's/^X//' -e "$sed_quote_subst"`
LC_ALL=C sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' <<EOF
# How to pass a linker flag through the compiler.
wl="$escaped_wl"
# Static library suffix (normally "a").
libext="$libext"
# Shared library suffix (normally "so").
shlibext="$shlibext"
# Format of library name prefix.
libname_spec="$escaped_libname_spec"
# Library names that the linker finds when passed -lNAME.
library_names_spec="$escaped_library_names_spec"
# Flag to hardcode \$libdir into a binary during linking.
# This must work even if \$libdir does not exist.
hardcode_libdir_flag_spec="$escaped_hardcode_libdir_flag_spec"
# Whether we need a single -rpath flag with a separated argument.
hardcode_libdir_separator="$hardcode_libdir_separator"
# Set to yes if using DIR/libNAME.so during linking hardcodes DIR into the
# resulting binary.
hardcode_direct="$hardcode_direct"
# Set to yes if using the -LDIR flag during linking hardcodes DIR into the
# resulting binary.
hardcode_minus_L="$hardcode_minus_L"
EOF

1793
2025-03-19/e2fsprogs-1.46.2/config/config.sub vendored Executable file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,238 @@
#! /bin/sh
#
# install - install a program, script, or datafile
# This comes from X11R5.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# `make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch.
#
# set DOITPROG to echo to test this script
# Don't use :- since 4.3BSD and earlier shells don't like it.
doit="${DOITPROG-}"
# put in absolute paths if you don't have them in your path; or use env. vars.
mvprog="${MVPROG-mv}"
cpprog="${CPPROG-cp}"
chmodprog="${CHMODPROG-chmod}"
chownprog="${CHOWNPROG-chown}"
chgrpprog="${CHGRPPROG-chgrp}"
stripprog="${STRIPPROG-strip}"
rmprog="${RMPROG-rm}"
mkdirprog="${MKDIRPROG-mkdir}"
tranformbasename=""
transform_arg=""
instcmd="$mvprog"
chmodcmd="$chmodprog 0755"
chowncmd=""
chgrpcmd=""
stripcmd=""
rmcmd="$rmprog -f"
mvcmd="$mvprog"
src=""
dst=""
dir_arg=""
while [ x"$1" != x ]; do
case $1 in
-c) instcmd="$cpprog"
shift
continue;;
-d) dir_arg=true
shift
continue;;
-m) chmodcmd="$chmodprog $2"
shift
shift
continue;;
-o) chowncmd="$chownprog $2"
shift
shift
continue;;
-g) chgrpcmd="$chgrpprog $2"
shift
shift
continue;;
-s) stripcmd="$stripprog"
shift
continue;;
-t=*) transformarg=`echo $1 | sed 's/-t=//'`
shift
continue;;
-b=*) transformbasename=`echo $1 | sed 's/-b=//'`
shift
continue;;
*) if [ x"$src" = x ]
then
src=$1
else
# this colon is to work around a 386BSD /bin/sh bug
:
dst=$1
fi
shift
continue;;
esac
done
if [ x"$src" = x ]
then
echo "install: no input file specified"
exit 1
else
true
fi
if [ x"$dir_arg" != x ]; then
dst=$src
src=""
if [ -d $dst ]; then
instcmd=:
else
instcmd=mkdir
fi
else
# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if [ -f $src -o -d $src ]
then
true
else
echo "install: $src does not exist"
exit 1
fi
if [ x"$dst" = x ]
then
echo "install: no destination specified"
exit 1
else
true
fi
# If destination is a directory, append the input filename; if your system
# does not like double slashes in filenames, you may need to add some logic
if [ -d $dst ]
then
dst="$dst"/`basename $src`
else
true
fi
fi
## this sed command emulates the dirname command
dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
# Make sure that the destination directory exists.
# this part is taken from Noah Friedman's mkinstalldirs script
# Skip lots of stat calls in the usual case.
if [ ! -d "$dstdir" ]; then
defaultIFS='
'
IFS="${IFS-${defaultIFS}}"
oIFS="${IFS}"
# Some sh's can't handle IFS=/ for some reason.
IFS='%'
set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
IFS="${oIFS}"
pathcomp=''
while [ $# -ne 0 ] ; do
pathcomp="${pathcomp}${1}"
shift
if [ ! -d "${pathcomp}" ] ;
then
$mkdirprog "${pathcomp}"
else
true
fi
pathcomp="${pathcomp}/"
done
fi
if [ x"$dir_arg" != x ]
then
$doit $instcmd $dst &&
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
else
# If we're going to rename the final executable, determine the name now.
if [ x"$transformarg" = x ]
then
dstfile=`basename $dst`
else
dstfile=`basename $dst $transformbasename |
sed $transformarg`$transformbasename
fi
# don't allow the sed command to completely eliminate the filename
if [ x"$dstfile" = x ]
then
dstfile=`basename $dst`
else
true
fi
# Make a temp file name in the proper directory.
dsttmp=$dstdir/#inst.$$#
# Move or copy the file name to the temp name
$doit $instcmd $src $dsttmp &&
trap "rm -f ${dsttmp}" 0 &&
# and set any options; do chmod last to preserve setuid bits
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $instcmd $src $dsttmp" command.
if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
# Now rename the file to the real destination.
$doit $rmcmd -f $dstdir/$dstfile &&
$doit $mvcmd $dsttmp $dstdir/$dstfile
fi &&
exit 0

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,40 @@
#! /bin/sh
# mkinstalldirs --- make directory hierarchy
# Author: Noah Friedman <friedman@prep.ai.mit.edu>
# Created: 1993-05-16
# Public domain
# $Id: mkinstalldirs,v 1.13 1999/01/05 03:18:55 bje Exp $
errstatus=0
for file
do
set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
shift
pathcomp=
for d
do
pathcomp="$pathcomp$d"
case "$pathcomp" in
-* ) pathcomp=./$pathcomp ;;
esac
if test ! -d "$pathcomp"; then
echo "mkdir $pathcomp"
mkdir "$pathcomp" || lasterr=$?
if test ! -d "$pathcomp"; then
errstatus=$lasterr
fi
fi
pathcomp="$pathcomp/"
done
done
exit $errstatus
# mkinstalldirs ends here

View file

@ -0,0 +1,127 @@
#!/bin/sh
cat > sed.script << "EOF"
/^#/d
/^$/d
s/__extension__ //
s/typedef \(.*\) __u\([1-9]*\);/#define __U\2_TYPEDEF \1/
s/typedef \(.*\) __s\([1-9]*\);/#define __S\2_TYPEDEF \1/
EOF
if test -z "$CC"; then
CC=gcc
fi
if test -z "$CPP"; then
CPP="$CC -E"
fi
/bin/echo -n "checking for __uNN types... "
# can't check [ -f /usr/include/asm/types.h ] directly, since
# the include path might be different if cross-compiling
if echo '#include <asm/types.h>' | $CPP - 2> parse-types.log | \
sed -f sed.script | grep '^#' > asm_types.h; then
echo "using <asm/types.h>"
else
echo "using generic types"
fi
rm sed.script
cp asm_types.h asm_types.c
cat >> asm_types.c <<EOF
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
#ifdef __U8_TYPEDEF
if (sizeof(__U8_TYPEDEF) != 1) {
printf("Sizeof(__U8__TYPEDEF) is %d should be 1\n",
(int) sizeof(__U8_TYPEDEF));
exit(1);
}
#elif defined(__linux__)
#warning __U8_TYPEDEF not defined
#endif
#ifdef __S8_TYPEDEF
if (sizeof(__S8_TYPEDEF) != 1) {
printf("Sizeof(_S8__TYPEDEF) is %d should be 1\n",
(int) sizeof(__S8_TYPEDEF));
exit(1);
}
#elif defined(__linux__)
#warning __S8_TYPEDEF not defined
#endif
#ifdef __U16_TYPEDEF
if (sizeof(__U16_TYPEDEF) != 2) {
printf("Sizeof(__U16__TYPEDEF) is %d should be 2\n",
(int) sizeof(__U16_TYPEDEF));
exit(1);
}
#elif defined(__linux__)
#warning __U16_TYPEDEF not defined
#endif
#ifdef __S16_TYPEDEF
if (sizeof(__S16_TYPEDEF) != 2) {
printf("Sizeof(__S16__TYPEDEF) is %d should be 2\n",
(int) sizeof(__S16_TYPEDEF));
exit(1);
}
#elif defined(__linux__)
#warning __S16_TYPEDEF not defined
#endif
#ifdef __U32_TYPEDEF
if (sizeof(__U32_TYPEDEF) != 4) {
printf("Sizeof(__U32__TYPEDEF) is %d should be 4\n",
(int) sizeof(__U32_TYPEDEF));
exit(1);
}
#elif defined(__linux__)
#warning __U32_TYPEDEF not defined
#endif
#ifdef __S32_TYPEDEF
if (sizeof(__S32_TYPEDEF) != 4) {
printf("Sizeof(__S32__TYPEDEF) is %d should be 4\n",
(int) sizeof(__S32_TYPEDEF));
exit(1);
}
#elif defined(__linux__)
#warning __S32_TYPEDEF not defined
#endif
#ifdef __U64_TYPEDEF
if (sizeof(__U64_TYPEDEF) != 8) {
printf("Sizeof(__U64__TYPEDEF) is %d should be 8\n",
(int) sizeof(__U64_TYPEDEF));
exit(1);
}
#elif defined(__linux__)
#warning __U64_TYPEDEF not defined
#endif
#ifdef __S64_TYPEDEF
if (sizeof(__S64_TYPEDEF) != 8) {
printf("Sizeof(__S64__TYPEDEF) is %d should be 8\n",
(int) sizeof(__S64_TYPEDEF));
exit(1);
}
#elif defined(__linux__)
#warning __S64_TYPEDEF not defined
#endif
return 0;
}
EOF
${BUILD_CC-${CC-gcc}} -o asm_types asm_types.c
if ./asm_types
then
true
else
if [ "${CROSS_COMPILE}" != "1" ]; then
echo "Problem detected with asm_types.h"
echo "" > asm_types.h
fi
fi
rm asm_types.c asm_types

14161
2025-03-19/e2fsprogs-1.46.2/configure vendored Executable file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,19 @@
// Copyright 2017 The Android Open Source Project
subdirs = ["android"]
//########################################################################
// Build add_ext4_encrypt
cc_binary {
name: "add_ext4_encrypt",
host_supported: true,
defaults: ["e2fsprogs-defaults"],
srcs: ["add_ext4_encrypt.c"],
shared_libs: [
"libext2fs",
"libext2_com_err",
],
system_shared_libs: ["libc", "libdl"],
}

View file

@ -0,0 +1,64 @@
/*
* Basic program to add ext4 encryption to a file system
*
* Copyright 2015, Google, Inc.
*
* %Begin-Header%
* This file may be redistributed under the terms of the GNU Public
* License.
* %End-Header%
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <sys/time.h>
#include <ext2fs/ext2_fs.h>
#include <ext2fs/ext2fs.h>
int main (int argc, char *argv[])
{
errcode_t retval = 0;
ext2_filsys fs;
setbuf(stdout, NULL);
setbuf(stderr, NULL);
initialize_ext2_error_table();
if (argc != 2) {
fprintf(stderr, "%s: Usage <device|filesystem>\n", argv[0]);
exit(1);
}
retval = ext2fs_open(argv[1], EXT2_FLAG_RW, 0, 0,
unix_io_manager, &fs);
if (retval) {
com_err(argv[0], retval, "while trying to open '%s'",
argv[1]);
exit(1);
}
if (!ext2fs_has_feature_encrypt(fs->super)) {
ext2fs_set_feature_encrypt(fs->super);
fs->super->s_encrypt_algos[0] =
EXT4_ENCRYPTION_MODE_AES_256_XTS;
fs->super->s_encrypt_algos[1] =
EXT4_ENCRYPTION_MODE_AES_256_CTS;
ext2fs_mark_super_dirty(fs);
printf("Ext4 encryption enabled on %s\n", argv[1]);
} else
printf("Ext4 encryption already enabled on %s\n", argv[1]);
retval = ext2fs_close(fs);
if (retval) {
com_err(argv[0], retval, "while trying to close '%s'",
argv[1]);
exit(1);
}
return (0);
}

View file

@ -0,0 +1,69 @@
// Copyright 2017 The Android Open Source Project
//##########################################################################
// Build e2fsdroid
cc_binary {
name: "e2fsdroid",
host_supported: true,
recovery_available: true,
defaults: ["e2fsprogs-defaults"],
srcs: [
"e2fsdroid.c",
"block_range.c",
"fsmap.c",
"block_list.c",
"base_fs.c",
"perms.c",
"basefs_allocator.c",
],
target: {
host: {
static_libs: [
"libext2_com_err",
"libext2_misc",
"libext2fs",
"libsparse",
"libz",
"libcutils",
"libbase",
"libselinux",
"libcrypto",
"liblog",
],
sanitize: {
address: false, // http://b/68387795 - heap overflow in e2fsdroid
},
},
android: {
shared_libs: [
"libext2fs",
"libext2_com_err",
"libext2_misc",
"libcutils",
"libbase",
"libselinux",
"libcrypto",
],
},
},
stl: "libc++_static",
}
//##########################################################################
// Build ext2simg
cc_binary {
name: "ext2simg",
host_supported: true,
defaults: ["e2fsprogs-defaults"],
srcs: ["ext2simg.c"],
shared_libs: [
"libext2fs",
"libext2_com_err",
"libsparse",
"libz",
],
}

View file

@ -0,0 +1,205 @@
#include "base_fs.h"
#include <stdio.h>
#define BASE_FS_VERSION "Base EXT4 version 1.0"
struct base_fs {
FILE *file;
const char *mountpoint;
struct basefs_entry entry;
};
static FILE *basefs_open(const char *file)
{
char *line = NULL;
size_t len;
FILE *f = fopen(file, "r");
if (!f)
return NULL;
if (getline(&line, &len, f) == -1 || !line)
goto err_getline;
if (strncmp(line, BASE_FS_VERSION, strlen(BASE_FS_VERSION)))
goto err_header;
free(line);
return f;
err_header:
free(line);
err_getline:
fclose(f);
return NULL;
}
static struct basefs_entry *basefs_readline(FILE *f, const char *mountpoint,
int *err)
{
char *line = NULL, *saveptr1, *saveptr2, *block_range, *block;
int offset;
size_t len;
struct basefs_entry *entry = NULL;
blk64_t range_start, range_end;
if (getline(&line, &len, f) == -1) {
if (feof(f))
goto end;
goto err_getline;
}
entry = calloc(1, sizeof(*entry));
if (!entry)
goto err_alloc;
/*
* With BASEFS version 1.0, a typical line looks like this:
* /bin/mke2fs 5000-5004,8000,9000-9990
*/
if (sscanf(line, "%ms%n", &entry->path, &offset) != 1)
goto err_sscanf;
len = strlen(mountpoint);
memmove(entry->path, entry->path + len, strlen(entry->path) - len + 1);
while (line[offset] == ' ')
++offset;
block_range = strtok_r(line + offset, ",\n", &saveptr1);
while (block_range) {
block = strtok_r(block_range, "-", &saveptr2);
if (!block)
break;
range_start = atoll(block);
block = strtok_r(NULL, "-", &saveptr2);
range_end = block ? atoll(block) : range_start;
add_blocks_to_range(&entry->blocks, range_start, range_end);
block_range = strtok_r(NULL, ",\n", &saveptr1);
}
end:
*err = 0;
free(line);
return entry;
err_sscanf:
free(entry);
err_alloc:
free(line);
err_getline:
*err = 1;
return NULL;
}
static void free_base_fs_entry(void *e)
{
struct basefs_entry *entry = e;
if (entry) {
free(entry->path);
free(entry);
}
}
struct ext2fs_hashmap *basefs_parse(const char *file, const char *mountpoint)
{
int err;
struct ext2fs_hashmap *entries = NULL;
struct basefs_entry *entry;
FILE *f = basefs_open(file);
if (!f)
return NULL;
entries = ext2fs_hashmap_create(ext2fs_djb2_hash, free_base_fs_entry, 1024);
if (!entries)
goto end;
while ((entry = basefs_readline(f, mountpoint, &err)))
ext2fs_hashmap_add(entries, entry, entry->path,
strlen(entry->path));
if (err) {
fclose(f);
ext2fs_hashmap_free(entries);
return NULL;
}
end:
fclose(f);
return entries;
}
static void *init(const char *file, const char *mountpoint)
{
struct base_fs *params = malloc(sizeof(*params));
if (!params)
return NULL;
params->mountpoint = mountpoint;
params->file = fopen(file, "w+");
if (!params->file) {
free(params);
return NULL;
}
if (fwrite(BASE_FS_VERSION"\n", 1, strlen(BASE_FS_VERSION"\n"),
params->file) != strlen(BASE_FS_VERSION"\n")) {
fclose(params->file);
free(params);
return NULL;
}
return params;
}
static int start_new_file(char *path, ext2_ino_t ino EXT2FS_ATTR((unused)),
struct ext2_inode *inode, void *data)
{
struct base_fs *params = data;
params->entry.path = LINUX_S_ISREG(inode->i_mode) ? path : NULL;
return 0;
}
static int add_block(ext2_filsys fs EXT2FS_ATTR((unused)), blk64_t blocknr,
int metadata, void *data)
{
struct base_fs *params = data;
if (params->entry.path && !metadata)
add_blocks_to_range(&params->entry.blocks, blocknr, blocknr);
return 0;
}
static int inline_data(void *inline_data EXT2FS_ATTR((unused)),
void *data EXT2FS_ATTR((unused)))
{
return 0;
}
static int end_new_file(void *data)
{
struct base_fs *params = data;
if (!params->entry.path)
return 0;
if (fprintf(params->file, "%s%s ", params->mountpoint,
params->entry.path) < 0
|| write_block_ranges(params->file, params->entry.blocks.head, ",")
|| fwrite("\n", 1, 1, params->file) != 1)
return -1;
delete_block_ranges(&params->entry.blocks);
return 0;
}
static int cleanup(void *data)
{
struct base_fs *params = data;
fclose(params->file);
free(params);
return 0;
}
struct fsmap_format base_fs_format = {
.init = init,
.start_new_file = start_new_file,
.add_block = add_block,
.inline_data = inline_data,
.end_new_file = end_new_file,
.cleanup = cleanup,
};

View file

@ -0,0 +1,17 @@
#ifndef BASE_FS_H
# define BASE_FS_H
# include "fsmap.h"
# include "hashmap.h"
# include "block_range.h"
struct basefs_entry {
char *path;
struct block_range_list blocks;
};
extern struct fsmap_format base_fs_format;
struct ext2fs_hashmap *basefs_parse(const char *file, const char *mountpoint);
#endif /* !BASE_FS_H */

View file

@ -0,0 +1,367 @@
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "basefs_allocator.h"
#include "block_range.h"
#include "hashmap.h"
#include "base_fs.h"
struct base_fs_allocator {
struct ext2fs_hashmap *entries;
struct basefs_entry *cur_entry;
/* The next expected logical block to allocate for cur_entry. */
blk64_t next_lblk;
/* Blocks which are definitely owned by a single inode in BaseFS. */
ext2fs_block_bitmap exclusive_block_map;
/* Blocks which are available to the first inode that requests it. */
ext2fs_block_bitmap dedup_block_map;
};
static errcode_t basefs_block_allocator(ext2_filsys, blk64_t, blk64_t *,
struct blk_alloc_ctx *ctx);
/*
* Free any reserved, but unconsumed block ranges in the allocator. This both
* frees the block_range_list data structure and unreserves exclusive blocks
* from the block map.
*/
static void fs_free_blocks_range(ext2_filsys fs,
struct base_fs_allocator *allocator,
struct block_range_list *list)
{
ext2fs_block_bitmap exclusive_map = allocator->exclusive_block_map;
blk64_t block;
while (list->head) {
block = consume_next_block(list);
if (ext2fs_test_block_bitmap2(exclusive_map, block)) {
ext2fs_unmark_block_bitmap2(fs->block_map, block);
ext2fs_unmark_block_bitmap2(exclusive_map, block);
}
}
}
/*
* Free any blocks in the bitmap that were reserved but never used. This is
* needed to free dedup_block_map and ensure the free block bitmap is
* internally consistent.
*/
static void fs_free_blocks_bitmap(ext2_filsys fs, ext2fs_block_bitmap bitmap)
{
blk64_t block = 0;
blk64_t start = fs->super->s_first_data_block;
blk64_t end = ext2fs_blocks_count(fs->super) - 1;
errcode_t retval;
for (;;) {
retval = ext2fs_find_first_set_block_bitmap2(bitmap, start, end,
&block);
if (retval)
break;
ext2fs_unmark_block_bitmap2(fs->block_map, block);
start = block + 1;
}
}
static void basefs_allocator_free(ext2_filsys fs,
struct base_fs_allocator *allocator)
{
struct basefs_entry *e;
struct ext2fs_hashmap_entry *it = NULL;
struct ext2fs_hashmap *entries = allocator->entries;
if (entries) {
while ((e = ext2fs_hashmap_iter_in_order(entries, &it))) {
fs_free_blocks_range(fs, allocator, &e->blocks);
delete_block_ranges(&e->blocks);
}
ext2fs_hashmap_free(entries);
}
fs_free_blocks_bitmap(fs, allocator->dedup_block_map);
ext2fs_free_block_bitmap(allocator->exclusive_block_map);
ext2fs_free_block_bitmap(allocator->dedup_block_map);
free(allocator);
}
/*
* Build a bitmap of which blocks are definitely owned by exactly one file in
* Base FS. Blocks which are not valid or are de-duplicated are skipped. This
* is called during allocator initialization, to ensure that libext2fs does
* not allocate which we want to re-use.
*
* If a block was allocated in the initial filesystem, it can never be re-used,
* so it will appear in neither the exclusive or dedup set. If a block is used
* by multiple files, it will be removed from the owned set and instead added
* to the dedup set.
*
* The dedup set is not removed from fs->block_map. This allows us to re-use
* dedup blocks separately and not have them be allocated outside of file data.
*/
static void fs_reserve_block(ext2_filsys fs,
struct base_fs_allocator *allocator,
blk64_t block)
{
ext2fs_block_bitmap exclusive_map = allocator->exclusive_block_map;
ext2fs_block_bitmap dedup_map = allocator->dedup_block_map;
if (block >= ext2fs_blocks_count(fs->super))
return;
if (ext2fs_test_block_bitmap2(fs->block_map, block)) {
if (!ext2fs_test_block_bitmap2(exclusive_map, block))
return;
ext2fs_unmark_block_bitmap2(exclusive_map, block);
ext2fs_mark_block_bitmap2(dedup_map, block);
} else {
ext2fs_mark_block_bitmap2(fs->block_map, block);
ext2fs_mark_block_bitmap2(exclusive_map, block);
}
}
static void fs_reserve_blocks_range(ext2_filsys fs,
struct base_fs_allocator *allocator,
struct block_range_list *list)
{
blk64_t block;
struct block_range *blocks = list->head;
while (blocks) {
for (block = blocks->start; block <= blocks->end; block++)
fs_reserve_block(fs, allocator, block);
blocks = blocks->next;
}
}
/*
* For each file in the base FS map, ensure that its blocks are reserved in
* the actual block map. This prevents libext2fs from allocating them for
* general purpose use, and ensures that if the file needs data blocks, they
* can be re-acquired exclusively for that file.
*
* If a file in the base map is missing, or not a regular file in the new
* filesystem, then it's skipped to ensure that its blocks are reusable.
*/
static errcode_t fs_reserve_blocks(ext2_filsys fs,
struct base_fs_allocator *allocator,
const char *src_dir)
{
int nbytes;
char full_path[PATH_MAX];
const char *sep = "/";
struct stat st;
struct basefs_entry *e;
struct ext2fs_hashmap_entry *it = NULL;
struct ext2fs_hashmap *entries = allocator->entries;
if (strlen(src_dir) && src_dir[strlen(src_dir) - 1] == '/')
sep = "";
while ((e = ext2fs_hashmap_iter_in_order(entries, &it))) {
nbytes = snprintf(full_path, sizeof(full_path), "%s%s%s",
src_dir, sep, e->path);
if (nbytes >= sizeof(full_path))
return ENAMETOOLONG;
if (lstat(full_path, &st) || !S_ISREG(st.st_mode))
continue;
fs_reserve_blocks_range(fs, allocator, &e->blocks);
}
return 0;
}
errcode_t base_fs_alloc_load(ext2_filsys fs, const char *file,
const char *mountpoint, const char *src_dir)
{
errcode_t retval = 0;
struct base_fs_allocator *allocator;
allocator = calloc(1, sizeof(*allocator));
if (!allocator) {
retval = ENOMEM;
goto out;
}
retval = ext2fs_read_bitmaps(fs);
if (retval)
goto err_load;
allocator->cur_entry = NULL;
allocator->entries = basefs_parse(file, mountpoint);
if (!allocator->entries) {
retval = EIO;
goto err_load;
}
retval = ext2fs_allocate_block_bitmap(fs, "exclusive map",
&allocator->exclusive_block_map);
if (retval)
goto err_load;
retval = ext2fs_allocate_block_bitmap(fs, "dedup map",
&allocator->dedup_block_map);
if (retval)
goto err_load;
retval = fs_reserve_blocks(fs, allocator, src_dir);
if (retval)
goto err_load;
/* Override the default allocator */
fs->get_alloc_block2 = basefs_block_allocator;
fs->priv_data = allocator;
goto out;
err_load:
basefs_allocator_free(fs, allocator);
out:
return retval;
}
/* Try and acquire the next usable block from the Base FS map. */
static errcode_t get_next_block(ext2_filsys fs, struct base_fs_allocator *allocator,
struct block_range_list* list, blk64_t *ret)
{
blk64_t block;
ext2fs_block_bitmap exclusive_map = allocator->exclusive_block_map;
ext2fs_block_bitmap dedup_map = allocator->dedup_block_map;
if (!list->head)
return EXT2_ET_BLOCK_ALLOC_FAIL;
block = consume_next_block(list);
if (block >= ext2fs_blocks_count(fs->super))
return EXT2_ET_BLOCK_ALLOC_FAIL;
if (ext2fs_test_block_bitmap2(exclusive_map, block)) {
ext2fs_unmark_block_bitmap2(exclusive_map, block);
*ret = block;
return 0;
}
if (ext2fs_test_block_bitmap2(dedup_map, block)) {
ext2fs_unmark_block_bitmap2(dedup_map, block);
*ret = block;
return 0;
}
return EXT2_ET_BLOCK_ALLOC_FAIL;
}
/*
* BaseFS lists blocks in logical block order. However, the allocator hook is
* only called if a block needs to be allocated. In the case of a deduplicated
* block, or a hole, the hook is not invoked. This means the next block
* allocation request will be out of sequence. For example, consider if BaseFS
* specifies the following (0 being a hole):
* 1 2 3 0 4 5
*
* If the new file has a hole at logical block 0, we could accidentally
* shift the entire expected block list as follows:
* 0 1 2 0 3 4
*
* To account for this, we track the next expected logical block in the
* allocator. If the current request is for a later logical block, we skip and
* free the intermediate physical blocks that would have been allocated. This
* ensures the original block assignment is respected.
*/
static void skip_blocks(ext2_filsys fs, struct base_fs_allocator *allocator,
struct blk_alloc_ctx *ctx)
{
blk64_t block;
struct block_range_list *list = &allocator->cur_entry->blocks;
ext2fs_block_bitmap exclusive_map = allocator->exclusive_block_map;
while (list->head && allocator->next_lblk < ctx->lblk) {
block = consume_next_block(list);
if (block >= ext2fs_blocks_count(fs->super))
continue;
if (ext2fs_test_block_bitmap2(exclusive_map, block)) {
ext2fs_unmark_block_bitmap2(exclusive_map, block);
ext2fs_unmark_block_bitmap2(fs->block_map, block);
}
allocator->next_lblk++;
}
}
static errcode_t basefs_block_allocator(ext2_filsys fs, blk64_t goal,
blk64_t *ret, struct blk_alloc_ctx *ctx)
{
errcode_t retval;
struct base_fs_allocator *allocator = fs->priv_data;
struct basefs_entry *e = allocator->cur_entry;
ext2fs_block_bitmap dedup_map = allocator->dedup_block_map;
if (e && ctx && (ctx->flags & BLOCK_ALLOC_DATA)) {
if (allocator->next_lblk < ctx->lblk)
skip_blocks(fs, allocator, ctx);
allocator->next_lblk = ctx->lblk + 1;
if (!get_next_block(fs, allocator, &e->blocks, ret))
return 0;
}
retval = ext2fs_new_block2(fs, goal, fs->block_map, ret);
if (!retval) {
ext2fs_mark_block_bitmap2(fs->block_map, *ret);
return 0;
}
if (retval != EXT2_ET_BLOCK_ALLOC_FAIL)
return retval;
/* Try to steal a block from the dedup pool. */
retval = ext2fs_find_first_set_block_bitmap2(dedup_map,
fs->super->s_first_data_block,
ext2fs_blocks_count(fs->super) - 1, ret);
if (!retval) {
ext2fs_unmark_block_bitmap2(dedup_map, *ret);
return 0;
}
/*
* As a last resort, take any block from our file's list. This
* risks bloating the diff, but means we are more likely to
* successfully build an image.
*/
while (e->blocks.head) {
if (!get_next_block(fs, allocator, &e->blocks, ret))
return 0;
}
return EXT2_ET_BLOCK_ALLOC_FAIL;
}
void base_fs_alloc_cleanup(ext2_filsys fs)
{
basefs_allocator_free(fs, fs->priv_data);
fs->priv_data = NULL;
fs->get_alloc_block2 = NULL;
}
errcode_t base_fs_alloc_set_target(ext2_filsys fs, const char *target_path,
const char *name EXT2FS_ATTR((unused)),
ext2_ino_t parent_ino EXT2FS_ATTR((unused)),
ext2_ino_t root EXT2FS_ATTR((unused)), mode_t mode)
{
struct base_fs_allocator *allocator = fs->priv_data;
if (mode != S_IFREG)
return 0;
if (allocator) {
allocator->cur_entry = ext2fs_hashmap_lookup(allocator->entries,
target_path,
strlen(target_path));
allocator->next_lblk = 0;
}
return 0;
}
errcode_t base_fs_alloc_unset_target(ext2_filsys fs,
const char *target_path EXT2FS_ATTR((unused)),
const char *name EXT2FS_ATTR((unused)),
ext2_ino_t parent_ino EXT2FS_ATTR((unused)),
ext2_ino_t root EXT2FS_ATTR((unused)), mode_t mode)
{
struct base_fs_allocator *allocator = fs->priv_data;
if (!allocator || !allocator->cur_entry || mode != S_IFREG)
return 0;
fs_free_blocks_range(fs, allocator, &allocator->cur_entry->blocks);
delete_block_ranges(&allocator->cur_entry->blocks);
return 0;
}

View file

@ -0,0 +1,16 @@
#ifndef BASE_FS_ALLOCATOR_H
# define BASE_FS_ALLOCATOR_H
# include <time.h>
# include <ext2fs/ext2fs.h>
errcode_t base_fs_alloc_load(ext2_filsys fs, const char *file,
const char *mountpoint, const char *src_dir);
void base_fs_alloc_cleanup(ext2_filsys fs);
errcode_t base_fs_alloc_set_target(ext2_filsys fs, const char *target_path,
const char *name, ext2_ino_t parent_ino, ext2_ino_t root, mode_t mode);
errcode_t base_fs_alloc_unset_target(ext2_filsys fs, const char *target_path,
const char *name, ext2_ino_t parent_ino, ext2_ino_t root, mode_t mode);
#endif /* !BASE_FS_ALLOCATOR_H */

View file

@ -0,0 +1,89 @@
#include "block_list.h"
#include "block_range.h"
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
struct block_list {
FILE *f;
const char *mountpoint;
const char *filename;
struct block_range_list blocks;
};
static void *init(const char *file, const char *mountpoint)
{
struct block_list *params = calloc(1, sizeof(*params));
if (!params)
return NULL;
params->mountpoint = mountpoint;
params->f = fopen(file, "w+");
if (!params->f) {
free(params);
return NULL;
}
return params;
}
static int start_new_file(char *path, ext2_ino_t ino EXT2FS_ATTR((unused)),
struct ext2_inode *inode EXT2FS_ATTR((unused)),
void *data)
{
struct block_list *params = data;
params->filename = LINUX_S_ISREG(inode->i_mode) ? path : NULL;
return 0;
}
static int add_block(ext2_filsys fs EXT2FS_ATTR((unused)), blk64_t blocknr,
int metadata, void *data)
{
struct block_list *params = data;
if (params->filename && !metadata)
add_blocks_to_range(&params->blocks, blocknr, blocknr);
return 0;
}
static int inline_data(void *inline_data EXT2FS_ATTR((unused)),
void *data EXT2FS_ATTR((unused)))
{
return 0;
}
static int end_new_file(void *data)
{
struct block_list *params = data;
if (!params->filename || !params->blocks.head)
return 0;
if (fprintf(params->f, "%s%s ", params->mountpoint,
params->filename) < 0
|| write_block_ranges(params->f, params->blocks.head, " ")
|| fwrite("\n", 1, 1, params->f) != 1)
return -1;
delete_block_ranges(&params->blocks);
return 0;
}
static int cleanup(void *data)
{
struct block_list *params = data;
fclose(params->f);
free(params);
return 0;
}
struct fsmap_format block_list_format = {
.init = init,
.start_new_file = start_new_file,
.add_block = add_block,
.inline_data = inline_data,
.end_new_file = end_new_file,
.cleanup = cleanup,
};

View file

@ -0,0 +1,8 @@
#ifndef BLOCK_LIST_H
# define BLOCK_LIST_H
# include "fsmap.h"
extern struct fsmap_format block_list_format;
#endif /* !BLOCK_LIST_H */

View file

@ -0,0 +1,80 @@
#define _GNU_SOURCE
#include "block_range.h"
#include <stdio.h>
struct block_range *new_block_range(blk64_t start, blk64_t end)
{
struct block_range *range = malloc(sizeof(*range));
range->start = start;
range->end = end;
range->next = NULL;
return range;
}
void add_blocks_to_range(struct block_range_list *list, blk64_t blk_start,
blk64_t blk_end)
{
if (list->head == NULL)
list->head = list->tail = new_block_range(blk_start, blk_end);
else if (list->tail->end + 1 == blk_start)
list->tail->end += (blk_end - blk_start + 1);
else {
struct block_range *range = new_block_range(blk_start, blk_end);
list->tail->next = range;
list->tail = range;
}
}
static void remove_head(struct block_range_list *list)
{
struct block_range *next_range = list->head->next;
free(list->head);
if (next_range == NULL)
list->head = list->tail = NULL;
else
list->head = next_range;
}
void delete_block_ranges(struct block_range_list *list)
{
while (list->head)
remove_head(list);
}
int write_block_ranges(FILE *f, struct block_range *range,
char *sep)
{
int len;
char *buf;
while (range) {
if (range->start == range->end)
len = asprintf(&buf, "%llu%s", range->start, sep);
else
len = asprintf(&buf, "%llu-%llu%s", range->start,
range->end, sep);
if (fwrite(buf, 1, len, f) != (size_t)len) {
free(buf);
return -1;
}
free(buf);
range = range->next;
}
len = strlen(sep);
if (fseek(f, -len, SEEK_CUR) == -len)
return -1;
return 0;
}
blk64_t consume_next_block(struct block_range_list *list)
{
blk64_t ret = list->head->start;
list->head->start += 1;
if (list->head->start > list->head->end)
remove_head(list);
return ret;
}

View file

@ -0,0 +1,29 @@
#ifndef BLOCK_RANGE_H
# define BLOCK_RANGE_H
# include <sys/types.h>
# include <ext2fs/ext2fs.h>
struct block_range {
blk64_t start;
blk64_t end;
struct block_range *next;
};
struct block_range_list {
struct block_range *head;
struct block_range *tail;
};
void add_blocks_to_range(struct block_range_list *list, blk64_t blk_start,
blk64_t blk_end);
void delete_block_ranges(struct block_range_list *list);
int write_block_ranges(FILE *f, struct block_range *range, char *sep);
/*
* Given a non-empty range list, return the next block and remove it from the
* list.
*/
blk64_t consume_next_block(struct block_range_list *list);
#endif /* !BLOCK_RANGE_H */

View file

@ -0,0 +1,377 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <getopt.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <ext2fs/ext2fs.h>
#include "perms.h"
#include "base_fs.h"
#include "block_list.h"
#include "basefs_allocator.h"
#include "create_inode.h"
#ifndef UID_GID_MAP_MAX_EXTENTS
/*
* The value is defined in linux/user_namspace.h.
* The value is (arbitrarily) 5 in 4.14 and earlier, or 340 in 4.15 and later.
* Here, the bigger value is taken. See also man user_namespace(7).
*/
#define UID_GID_MAP_MAX_EXTENTS 340
#endif
static char *prog_name = "e2fsdroid";
static char *in_file;
static char *block_list;
static char *basefs_out;
static char *basefs_in;
static char *mountpoint = "";
static time_t fixed_time = -1;
static char *fs_config_file;
static struct selinux_opt seopt_file[8];
static int max_nr_opt = (int)sizeof(seopt_file) / sizeof(seopt_file[0]);
static char *product_out;
static char *src_dir;
static int android_configure;
static int android_sparse_file = 1;
static void usage(int ret)
{
fprintf(stderr, "%s [-B block_list] [-D basefs_out] [-T timestamp]\n"
"\t[-C fs_config] [-S file_contexts] [-p product_out]\n"
"\t[-a mountpoint] [-d basefs_in] [-f src_dir] [-e] [-s]\n"
"\t[-u uid-mapping] [-g gid-mapping] image\n",
prog_name);
exit(ret);
}
static char *absolute_path(const char *file)
{
char *ret;
char cwd[PATH_MAX];
if (file[0] != '/') {
if (getcwd(cwd, PATH_MAX) == NULL) {
fprintf(stderr, "Failed to getcwd\n");
exit(EXIT_FAILURE);
}
ret = malloc(strlen(cwd) + 1 + strlen(file) + 1);
if (ret)
sprintf(ret, "%s/%s", cwd, file);
} else
ret = strdup(file);
return ret;
}
static int parse_ugid_map_entry(char* line, struct ugid_map_entry* result)
{
char *token, *token_saveptr;
size_t num_tokens;
unsigned int *parsed[] = {&result->child_id,
&result->parent_id,
&result->length};
for (token = strtok_r(line, " ", &token_saveptr), num_tokens = 0;
token && num_tokens < 3;
token = strtok_r(NULL, " ", &token_saveptr), ++num_tokens) {
char* endptr = NULL;
unsigned long t = strtoul(token, &endptr, 10);
if ((t == ULONG_MAX && errno) || (t > UINT_MAX) || *endptr) {
fprintf(stderr, "Malformed u/gid mapping line\n");
return 0;
}
*parsed[num_tokens] = (unsigned int) t;
}
if (num_tokens < 3 || strtok_r(NULL, " ", &token_saveptr) != NULL) {
fprintf(stderr, "Malformed u/gid mapping line\n");
return 0;
}
if (result->child_id + result->length < result->child_id ||
result->parent_id + result->length < result->parent_id) {
fprintf(stderr, "u/gid mapping overflow\n");
return 0;
}
return 1;
}
/*
* Returns 1 if [begin1, begin1+length1) and [begin2, begin2+length2) have
* overlapping range. Otherwise 0.
*/
static int is_overlapping(unsigned int begin1, unsigned int length1,
unsigned int begin2, unsigned int length2)
{
unsigned int end1 = begin1 + length1;
unsigned int end2 = begin2 + length2;
return !(end1 <= begin2 || end2 <= begin1);
}
/*
* Verifies if the given mapping works.
* - Checks if the number of entries is less than or equals to
* UID_GID_MAP_MAX_EXTENTS.
* - Checks if there is no overlapped ranges.
* Returns 1 if valid, otherwise 0.
*/
static int is_valid_ugid_map(const struct ugid_map* mapping)
{
size_t i, j;
if (mapping->size > UID_GID_MAP_MAX_EXTENTS) {
fprintf(stderr, "too many u/gid mapping entries\n");
return 0;
}
for (i = 0; i < mapping->size; ++i) {
const struct ugid_map_entry *entry1 = &mapping->entries[i];
for (j = i + 1; j < mapping->size; ++j) {
const struct ugid_map_entry *entry2 =
&mapping->entries[j];
if (is_overlapping(entry1->child_id, entry1->length,
entry2->child_id, entry2->length)) {
fprintf(stderr,
"Overlapping child u/gid: [%d %d %d],"
" [%d %d %d]\n",
entry1->child_id, entry1->parent_id,
entry1->length, entry2->child_id,
entry2->parent_id, entry2->length);
return 0;
}
if (is_overlapping(entry1->parent_id, entry1->length,
entry2->parent_id, entry2->length)) {
fprintf(stderr,
"Overlapping parent u/gid: [%d %d %d],"
" [%d %d %d]\n",
entry1->child_id, entry1->parent_id,
entry1->length, entry2->child_id,
entry2->parent_id, entry2->length);
return 0;
}
}
}
return 1;
}
/*
* Parses the UID/GID mapping argument. The argument could be a multi-line
* string (separated by '\n', no trailing '\n' is allowed). Each line must
* contain exact three integer tokens; the first token is |child_id|,
* the second is |parent_id|, and the last is |length| of the mapping range.
* See also user_namespace(7) man page.
* On success, the parsed entries are stored in |result|, and it returns 1.
* Otherwise, returns 0.
*/
static int parse_ugid_map(char* arg, struct ugid_map* result)
{
int i;
char *line, *line_saveptr;
size_t current_index;
/* Count the number of lines. */
result->size = 1;
for (i = 0; arg[i]; ++i) {
if (arg[i] == '\n')
++result->size;
}
/* Allocate memory for entries. */
result->entries = malloc(sizeof(struct ugid_map_entry) * result->size);
if (!result->entries) {
result->size = 0;
return 0;
}
/* Parse each line */
for (line = strtok_r(arg, "\n", &line_saveptr), current_index = 0;
line;
line = strtok_r(NULL, "\n", &line_saveptr), ++current_index) {
if (!parse_ugid_map_entry(
line, &result->entries[current_index])) {
return 0;
}
}
return is_valid_ugid_map(result);
}
int main(int argc, char *argv[])
{
int c;
char *p;
int flags = EXT2_FLAG_RW;
errcode_t retval;
io_manager io_mgr;
ext2_filsys fs = NULL;
struct fs_ops_callbacks fs_callbacks = { NULL, NULL };
char *token;
int nr_opt = 0;
ext2_ino_t inodes_count;
ext2_ino_t free_inodes_count;
blk64_t blocks_count;
blk64_t free_blocks_count;
struct ugid_map uid_map = { 0, NULL }, gid_map = { 0, NULL };
add_error_table(&et_ext2_error_table);
while ((c = getopt (argc, argv, "T:C:S:p:a:D:d:B:f:esu:g:")) != EOF) {
switch (c) {
case 'T':
fixed_time = strtoul(optarg, &p, 0);
android_configure = 1;
break;
case 'C':
fs_config_file = absolute_path(optarg);
android_configure = 1;
break;
case 'S':
token = strtok(optarg, ",");
while (token) {
if (nr_opt == max_nr_opt) {
fprintf(stderr, "Expected at most %d selinux opts\n",
max_nr_opt);
exit(EXIT_FAILURE);
}
seopt_file[nr_opt].type = SELABEL_OPT_PATH;
seopt_file[nr_opt].value = absolute_path(token);
nr_opt++;
token = strtok(NULL, ",");
}
android_configure = 1;
break;
case 'p':
product_out = absolute_path(optarg);
android_configure = 1;
break;
case 'a':
mountpoint = strdup(optarg);
break;
case 'D':
basefs_out = absolute_path(optarg);
break;
case 'd':
basefs_in = absolute_path(optarg);
break;
case 'B':
block_list = absolute_path(optarg);
break;
case 'f':
src_dir = absolute_path(optarg);
break;
case 'e':
android_sparse_file = 0;
break;
case 's':
flags |= EXT2_FLAG_SHARE_DUP;
break;
case 'u':
if (!parse_ugid_map(optarg, &uid_map))
exit(EXIT_FAILURE);
android_configure = 1;
break;
case 'g':
if (!parse_ugid_map(optarg, &gid_map))
exit(EXIT_FAILURE);
android_configure = 1;
break;
default:
usage(EXIT_FAILURE);
}
}
if (optind >= argc) {
fprintf(stderr, "Expected filename after options\n");
exit(EXIT_FAILURE);
}
if (android_sparse_file) {
io_mgr = sparse_io_manager;
if (asprintf(&in_file, "(%s)", argv[optind]) == -1) {
fprintf(stderr, "Failed to allocate file name\n");
exit(EXIT_FAILURE);
}
} else {
io_mgr = unix_io_manager;
in_file = strdup(argv[optind]);
}
retval = ext2fs_open(in_file, flags, 0, 0, io_mgr, &fs);
if (retval) {
com_err(prog_name, retval, "while opening file %s\n", in_file);
return retval;
}
if (src_dir) {
ext2fs_read_bitmaps(fs);
if (basefs_in) {
retval = base_fs_alloc_load(fs, basefs_in, mountpoint,
src_dir);
if (retval) {
com_err(prog_name, retval, "%s",
"while reading base_fs file");
exit(1);
}
fs_callbacks.create_new_inode =
base_fs_alloc_set_target;
fs_callbacks.end_create_new_inode =
base_fs_alloc_unset_target;
}
retval = populate_fs2(fs, EXT2_ROOT_INO, src_dir,
EXT2_ROOT_INO, &fs_callbacks);
if (retval) {
com_err(prog_name, retval, "%s",
"while populating file system");
exit(1);
}
if (basefs_in)
base_fs_alloc_cleanup(fs);
}
if (android_configure) {
retval = android_configure_fs(
fs, src_dir, product_out, mountpoint, seopt_file,
nr_opt, fs_config_file, fixed_time, &uid_map, &gid_map);
if (retval) {
com_err(prog_name, retval, "%s",
"while configuring the file system");
exit(1);
}
}
if (block_list) {
retval = fsmap_iter_filsys(fs, &block_list_format, block_list,
mountpoint);
if (retval) {
com_err(prog_name, retval, "%s",
"while creating the block_list");
exit(1);
}
}
if (basefs_out) {
retval = fsmap_iter_filsys(fs, &base_fs_format,
basefs_out, mountpoint);
if (retval) {
com_err(prog_name, retval, "%s",
"while creating the basefs file");
exit(1);
}
}
inodes_count = fs->super->s_inodes_count;
free_inodes_count = fs->super->s_free_inodes_count;
blocks_count = ext2fs_blocks_count(fs->super);
free_blocks_count = ext2fs_free_blocks_count(fs->super);
retval = ext2fs_close_free(&fs);
if (retval) {
com_err(prog_name, retval, "%s",
"while writing superblocks");
exit(1);
}
printf("Created filesystem with %u/%u inodes and %llu/%llu blocks\n",
inodes_count - free_inodes_count, inodes_count,
blocks_count - free_blocks_count, blocks_count);
remove_error_table(&et_ext2_error_table);
return 0;
}

View file

@ -0,0 +1,224 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <libgen.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ext2fs/ext2fs.h>
#include <et/com_err.h>
#include <sparse/sparse.h>
struct {
int crc;
int sparse;
int gzip;
char *in_file;
char *out_file;
bool overwrite_input;
} params = {
.crc = 0,
.sparse = 1,
.gzip = 0,
};
#define ext2fs_fatal(Retval, Format, ...) \
do { \
com_err("error", Retval, Format, __VA_ARGS__); \
exit(EXIT_FAILURE); \
} while(0)
#define sparse_fatal(Format) \
do { \
fprintf(stderr, "sparse: "Format); \
exit(EXIT_FAILURE); \
} while(0)
static void usage(char *path)
{
char *progname = basename(path);
fprintf(stderr, "%s [ options ] <image or block device> <output image>\n"
" -c include CRC block\n"
" -z gzip output\n"
" -S don't use sparse output format\n", progname);
}
static struct buf_item {
struct buf_item *next;
void *buf[0];
} *buf_list;
static void add_chunk(ext2_filsys fs, struct sparse_file *s, blk_t chunk_start, blk_t chunk_end)
{
int retval;
unsigned int nb_blk = chunk_end - chunk_start;
size_t len = nb_blk * fs->blocksize;
int64_t offset = (int64_t)chunk_start * (int64_t)fs->blocksize;
if (params.overwrite_input == false) {
if (sparse_file_add_file(s, params.in_file, offset, len, chunk_start) < 0)
sparse_fatal("adding data to the sparse file");
} else {
/*
* The input file will be overwritten, make a copy of
* the blocks
*/
struct buf_item *bi = calloc(1, sizeof(struct buf_item) + len);
if (buf_list == NULL)
buf_list = bi;
else {
bi->next = buf_list;
buf_list = bi;
}
retval = io_channel_read_blk64(fs->io, chunk_start, nb_blk, bi->buf);
if (retval < 0)
ext2fs_fatal(retval, "reading block %u - %u", chunk_start, chunk_end);
if (sparse_file_add_data(s, bi->buf, len, chunk_start) < 0)
sparse_fatal("adding data to the sparse file");
}
}
static void free_chunks(void)
{
struct buf_item *bi;
while (buf_list) {
bi = buf_list->next;
free(buf_list);
buf_list = bi;
}
}
static struct sparse_file *ext_to_sparse(const char *in_file)
{
errcode_t retval;
ext2_filsys fs;
struct sparse_file *s;
int64_t chunk_start = -1;
blk_t first_blk, last_blk, nb_blk, cur_blk;
retval = ext2fs_open(in_file, 0, 0, 0, unix_io_manager, &fs);
if (retval)
ext2fs_fatal(retval, "while reading %s", in_file);
retval = ext2fs_read_block_bitmap(fs);
if (retval)
ext2fs_fatal(retval, "while reading block bitmap of %s", in_file);
first_blk = ext2fs_get_block_bitmap_start2(fs->block_map);
last_blk = ext2fs_get_block_bitmap_end2(fs->block_map);
nb_blk = last_blk - first_blk + 1;
s = sparse_file_new(fs->blocksize, (uint64_t)fs->blocksize * (uint64_t)nb_blk);
if (!s)
sparse_fatal("creating sparse file");
/*
* The sparse format encodes the size of a chunk (and its header) in a
* 32-bit unsigned integer (UINT32_MAX)
* When writing the chunk, the library uses a single call to write().
* Linux's implementation of the 'write' syscall does not allow transfers
* larger than INT32_MAX (32-bit _and_ 64-bit systems).
* Make sure we do not create chunks larger than this limit.
*/
int64_t max_blk_per_chunk = (INT32_MAX - 12) / fs->blocksize;
/* Iter on the blocks to merge contiguous chunk */
for (cur_blk = first_blk; cur_blk <= last_blk; ++cur_blk) {
if (ext2fs_test_block_bitmap2(fs->block_map, cur_blk)) {
if (chunk_start == -1) {
chunk_start = cur_blk;
} else if (cur_blk - chunk_start + 1 == max_blk_per_chunk) {
add_chunk(fs, s, chunk_start, cur_blk);
chunk_start = -1;
}
} else if (chunk_start != -1) {
add_chunk(fs, s, chunk_start, cur_blk);
chunk_start = -1;
}
}
if (chunk_start != -1)
add_chunk(fs, s, chunk_start, cur_blk - 1);
ext2fs_free(fs);
return s;
}
static bool same_file(const char *in, const char *out)
{
struct stat st1, st2;
if (access(out, F_OK) == -1)
return false;
if (lstat(in, &st1) == -1)
ext2fs_fatal(errno, "stat %s\n", in);
if (lstat(out, &st2) == -1)
ext2fs_fatal(errno, "stat %s\n", out);
return st1.st_ino == st2.st_ino;
}
int main(int argc, char *argv[])
{
int opt;
int out_fd;
struct sparse_file *s;
while ((opt = getopt(argc, argv, "czS")) != -1) {
switch(opt) {
case 'c':
params.crc = 1;
break;
case 'z':
params.gzip = 1;
break;
case 'S':
params.sparse = 0;
break;
default:
usage(argv[0]);
exit(EXIT_FAILURE);
}
}
if (optind + 1 >= argc) {
usage(argv[0]);
exit(EXIT_FAILURE);
}
params.in_file = strdup(argv[optind++]);
params.out_file = strdup(argv[optind]);
params.overwrite_input = same_file(params.in_file, params.out_file);
s = ext_to_sparse(params.in_file);
out_fd = open(params.out_file, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if (out_fd == -1)
ext2fs_fatal(errno, "opening %s\n", params.out_file);
if (sparse_file_write(s, out_fd, params.gzip, params.sparse, params.crc) < 0)
sparse_fatal("writing sparse file");
sparse_file_destroy(s);
free(params.in_file);
free(params.out_file);
free_chunks();
close(out_fd);
return 0;
}

View file

@ -0,0 +1,169 @@
#include "fsmap.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "support/nls-enable.h"
struct walk_ext_priv_data {
char *path;
ext2_filsys fs;
struct fsmap_format *format;
};
static int walk_block(ext2_filsys fs EXT2FS_ATTR((unused)), blk64_t *blocknr,
e2_blkcnt_t blockcnt,
blk64_t ref64_blk EXT2FS_ATTR((unused)),
int ref_offset EXT2FS_ATTR((unused)),
void *priv)
{
struct walk_ext_priv_data *pdata = priv;
struct fsmap_format *format = pdata->format;
return format->add_block(fs, *blocknr, blockcnt < 0, format->private);
}
static errcode_t ino_iter_extents(ext2_filsys fs, ext2_ino_t ino,
ext2_extent_handle_t extents,
struct walk_ext_priv_data *pdata)
{
blk64_t block;
errcode_t retval;
blk64_t next_lblk = 0;
int op = EXT2_EXTENT_ROOT;
struct ext2fs_extent extent;
struct fsmap_format *format = pdata->format;
for (;;) {
retval = ext2fs_extent_get(extents, op, &extent);
if (retval)
break;
op = EXT2_EXTENT_NEXT;
if ((extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT) ||
!(extent.e_flags & EXT2_EXTENT_FLAGS_LEAF))
continue;
for (; next_lblk < extent.e_lblk; next_lblk++)
format->add_block(fs, 0, 0, format->private);
block = extent.e_pblk;
for (; next_lblk < extent.e_lblk + extent.e_len; next_lblk++)
format->add_block(fs, block++, 0, format->private);
}
if (retval == EXT2_ET_EXTENT_NO_NEXT)
retval = 0;
if (retval) {
com_err(__func__, retval, ("getting extents of ino \"%u\""),
ino);
}
return retval;
}
static errcode_t ino_iter_blocks(ext2_filsys fs, ext2_ino_t ino,
struct walk_ext_priv_data *pdata)
{
errcode_t retval;
struct ext2_inode inode;
ext2_extent_handle_t extents;
struct fsmap_format *format = pdata->format;
retval = ext2fs_read_inode(fs, ino, &inode);
if (retval)
return retval;
if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
return format->inline_data(&(inode.i_block[0]),
format->private);
retval = ext2fs_extent_open(fs, ino, &extents);
if (retval == EXT2_ET_INODE_NOT_EXTENT) {
retval = ext2fs_block_iterate3(fs, ino, BLOCK_FLAG_READ_ONLY,
NULL, walk_block, pdata);
if (retval) {
com_err(__func__, retval, _("listing blocks of ino \"%u\""),
ino);
}
return retval;
}
retval = ino_iter_extents(fs, ino, extents, pdata);
ext2fs_extent_free(extents);
return retval;
}
static int is_dir(ext2_filsys fs, ext2_ino_t ino)
{
struct ext2_inode inode;
if (ext2fs_read_inode(fs, ino, &inode))
return 0;
return S_ISDIR(inode.i_mode);
}
static int walk_ext_dir(ext2_ino_t dir EXT2FS_ATTR((unused)),
int flags EXT2FS_ATTR((unused)),
struct ext2_dir_entry *de,
int offset EXT2FS_ATTR((unused)),
int blocksize EXT2FS_ATTR((unused)),
char *buf EXT2FS_ATTR((unused)), void *priv_data)
{
errcode_t retval;
struct ext2_inode inode;
char *filename, *cur_path, *name = de->name;
int name_len = de->name_len & 0xff;
struct walk_ext_priv_data *pdata = priv_data;
struct fsmap_format *format = pdata->format;
if (!strncmp(name, ".", name_len)
|| !strncmp(name, "..", name_len)
|| !strncmp(name, "lost+found", 10))
return 0;
if (asprintf(&filename, "%s/%.*s", pdata->path, name_len, name) < 0)
return -ENOMEM;
retval = ext2fs_read_inode(pdata->fs, de->inode, &inode);
if (retval) {
com_err(__func__, retval, _("reading ino \"%u\""), de->inode);
goto end;
}
format->start_new_file(filename, de->inode, &inode, format->private);
retval = ino_iter_blocks(pdata->fs, de->inode, pdata);
if (retval)
return retval;
format->end_new_file(format->private);
retval = 0;
if (is_dir(pdata->fs, de->inode)) {
cur_path = pdata->path;
pdata->path = filename;
retval = ext2fs_dir_iterate2(pdata->fs, de->inode, 0, NULL,
walk_ext_dir, pdata);
pdata->path = cur_path;
}
end:
free(filename);
return retval;
}
errcode_t fsmap_iter_filsys(ext2_filsys fs, struct fsmap_format *format,
const char *file, const char *mountpoint)
{
struct walk_ext_priv_data pdata;
errcode_t retval;
format->private = format->init(file, mountpoint);
pdata.fs = fs;
pdata.path = "";
pdata.format = format;
retval = ext2fs_dir_iterate2(fs, EXT2_ROOT_INO, 0, NULL, walk_ext_dir, &pdata);
format->cleanup(format->private);
return retval;
}

View file

@ -0,0 +1,29 @@
#ifndef FSMAP_H
# define FSMAP_H
# ifndef _GNU_SOURCE
# define _GNU_SOURCE // asprintf
# endif
# include <stdio.h>
# include <stdint.h>
# include <stdbool.h>
# include <sys/types.h>
# include <ext2fs/ext2fs.h>
struct fsmap_format {
void* (* init)(const char *file, const char *mountpoint);
int (* start_new_file)(char *path, ext2_ino_t ino,
struct ext2_inode *inode, void *data);
int (* add_block)(ext2_filsys fs, blk64_t blocknr, int metadata,
void *data);
int (* inline_data)(void *inline_data, void *data);
int (* end_new_file)(void *data);
int (* cleanup)(void *data);
void *private;
};
errcode_t fsmap_iter_filsys(ext2_filsys fs, struct fsmap_format *format,
const char *file, const char *mountpoint);
#endif /* !FSMAP_H */

View file

@ -0,0 +1,375 @@
#ifndef _GNU_SOURCE
# define _GNU_SOURCE //asprintf
#endif
#include "perms.h"
#include "support/nls-enable.h"
#include <time.h>
#include <sys/stat.h>
#ifndef XATTR_SELINUX_SUFFIX
# define XATTR_SELINUX_SUFFIX "selinux"
#endif
#ifndef XATTR_CAPS_SUFFIX
# define XATTR_CAPS_SUFFIX "capability"
#endif
struct inode_params {
ext2_filsys fs;
char *path;
char *filename;
char *src_dir;
char *target_out;
char *mountpoint;
fs_config_f fs_config_func;
struct selabel_handle *sehnd;
time_t fixed_time;
const struct ugid_map* uid_map;
const struct ugid_map* gid_map;
errcode_t error;
};
static errcode_t ino_add_xattr(ext2_filsys fs, ext2_ino_t ino, const char *name,
const void *value, int value_len)
{
errcode_t retval, close_retval;
struct ext2_xattr_handle *xhandle;
retval = ext2fs_xattrs_open(fs, ino, &xhandle);
if (retval) {
com_err(__func__, retval, _("while opening inode %u"), ino);
return retval;
}
retval = ext2fs_xattrs_read(xhandle);
if (retval) {
com_err(__func__, retval,
_("while reading xattrs of inode %u"), ino);
goto xattrs_close;
}
retval = ext2fs_xattr_set(xhandle, name, value, value_len);
if (retval) {
com_err(__func__, retval,
_("while setting xattrs of inode %u"), ino);
goto xattrs_close;
}
xattrs_close:
close_retval = ext2fs_xattrs_close(&xhandle);
if (close_retval) {
com_err(__func__, close_retval,
_("while closing xattrs of inode %u"), ino);
return retval ? retval : close_retval;
}
return retval;
}
static errcode_t set_selinux_xattr(ext2_filsys fs, ext2_ino_t ino,
struct inode_params *params)
{
errcode_t retval;
char *secontext = NULL;
struct ext2_inode inode;
if (params->sehnd == NULL)
return 0;
retval = ext2fs_read_inode(fs, ino, &inode);
if (retval) {
com_err(__func__, retval,
_("while reading inode %u"), ino);
return retval;
}
retval = selabel_lookup(params->sehnd, &secontext, params->filename,
inode.i_mode);
if (retval < 0) {
int saved_errno = errno;
com_err(__func__, errno,
_("searching for label \"%s\""), params->filename);
return saved_errno;
}
retval = ino_add_xattr(fs, ino, "security." XATTR_SELINUX_SUFFIX,
secontext, strlen(secontext) + 1);
freecon(secontext);
return retval;
}
/*
* Returns mapped UID/GID if there is a corresponding entry in |mapping|.
* Otherwise |id| as is.
*/
static unsigned int resolve_ugid(const struct ugid_map* mapping,
unsigned int id)
{
size_t i;
for (i = 0; i < mapping->size; ++i) {
const struct ugid_map_entry* entry = &mapping->entries[i];
if (entry->parent_id <= id &&
id < entry->parent_id + entry->length) {
return id + entry->child_id - entry->parent_id;
}
}
/* No entry is found. */
return id;
}
static errcode_t set_perms_and_caps(ext2_filsys fs, ext2_ino_t ino,
struct inode_params *params)
{
errcode_t retval;
uint64_t capabilities = 0;
struct ext2_inode inode;
struct vfs_cap_data cap_data;
unsigned int uid = 0, gid = 0, imode = 0;
retval = ext2fs_read_inode(fs, ino, &inode);
if (retval) {
com_err(__func__, retval, _("while reading inode %u"), ino);
return retval;
}
/* Permissions */
if (params->fs_config_func != NULL) {
const char *filename = params->filename;
if (strcmp(filename, params->mountpoint) == 0) {
/* The root of the filesystem needs to be an empty string. */
filename = "";
}
params->fs_config_func(filename, S_ISDIR(inode.i_mode),
params->target_out, &uid, &gid, &imode,
&capabilities);
uid = resolve_ugid(params->uid_map, uid);
gid = resolve_ugid(params->gid_map, gid);
inode.i_uid = (__u16) uid;
inode.i_gid = (__u16) gid;
ext2fs_set_i_uid_high(inode, (__u16) (uid >> 16));
ext2fs_set_i_gid_high(inode, (__u16) (gid >> 16));
inode.i_mode = (inode.i_mode & S_IFMT) | (imode & 0xffff);
retval = ext2fs_write_inode(fs, ino, &inode);
if (retval) {
com_err(__func__, retval,
_("while writing inode %u"), ino);
return retval;
}
}
/* Capabilities */
if (!capabilities)
return 0;
memset(&cap_data, 0, sizeof(cap_data));
cap_data.magic_etc = VFS_CAP_REVISION_2 | VFS_CAP_FLAGS_EFFECTIVE;
cap_data.data[0].permitted = (uint32_t) (capabilities & 0xffffffff);
cap_data.data[1].permitted = (uint32_t) (capabilities >> 32);
return ino_add_xattr(fs, ino, "security." XATTR_CAPS_SUFFIX,
&cap_data, sizeof(cap_data));
}
static errcode_t set_timestamp(ext2_filsys fs, ext2_ino_t ino,
struct inode_params *params)
{
errcode_t retval;
struct ext2_inode inode;
struct stat stat;
char *src_filename = NULL;
retval = ext2fs_read_inode(fs, ino, &inode);
if (retval) {
com_err(__func__, retval,
_("while reading inode %u"), ino);
return retval;
}
if (params->fixed_time == -1 && params->src_dir) {
/* replace mountpoint from filename with src_dir */
if (asprintf(&src_filename, "%s/%s", params->src_dir,
params->filename + strlen(params->mountpoint)) < 0) {
return -ENOMEM;
}
retval = lstat(src_filename, &stat);
if (retval < 0) {
com_err(__func__, errno,
_("while lstat file %s"), src_filename);
goto end;
}
inode.i_atime = inode.i_ctime = inode.i_mtime = stat.st_mtime;
} else {
inode.i_atime = inode.i_ctime = inode.i_mtime = params->fixed_time;
}
retval = ext2fs_write_inode(fs, ino, &inode);
if (retval) {
com_err(__func__, retval,
_("while writing inode %u"), ino);
goto end;
}
end:
free(src_filename);
return retval;
}
static int is_dir(ext2_filsys fs, ext2_ino_t ino)
{
struct ext2_inode inode;
if (ext2fs_read_inode(fs, ino, &inode))
return 0;
return S_ISDIR(inode.i_mode);
}
static errcode_t androidify_inode(ext2_filsys fs, ext2_ino_t ino,
struct inode_params *params)
{
errcode_t retval;
retval = set_timestamp(fs, ino, params);
if (retval)
return retval;
retval = set_selinux_xattr(fs, ino, params);
if (retval)
return retval;
return set_perms_and_caps(fs, ino, params);
}
static int walk_dir(ext2_ino_t dir EXT2FS_ATTR((unused)),
int flags EXT2FS_ATTR((unused)),
struct ext2_dir_entry *de,
int offset EXT2FS_ATTR((unused)),
int blocksize EXT2FS_ATTR((unused)),
char *buf EXT2FS_ATTR((unused)), void *priv_data)
{
__u16 name_len;
errcode_t retval;
struct inode_params *params = (struct inode_params *)priv_data;
name_len = de->name_len & 0xff;
if (!strncmp(de->name, ".", name_len)
|| (!strncmp(de->name, "..", name_len)))
return 0;
if (asprintf(&params->filename, "%s/%.*s", params->path, name_len,
de->name) < 0) {
params->error = ENOMEM;
return -ENOMEM;
}
if (!strncmp(de->name, "lost+found", 10)) {
retval = set_selinux_xattr(params->fs, de->inode, params);
if (retval)
goto end;
} else {
retval = androidify_inode(params->fs, de->inode, params);
if (retval)
goto end;
if (is_dir(params->fs, de->inode)) {
char *cur_path = params->path;
char *cur_filename = params->filename;
params->path = params->filename;
retval = ext2fs_dir_iterate2(params->fs, de->inode, 0, NULL,
walk_dir, params);
if (retval)
goto end;
params->path = cur_path;
params->filename = cur_filename;
}
}
end:
free(params->filename);
params->error |= retval;
return retval;
}
errcode_t __android_configure_fs(ext2_filsys fs, char *src_dir,
char *target_out,
char *mountpoint,
fs_config_f fs_config_func,
struct selabel_handle *sehnd,
time_t fixed_time,
const struct ugid_map* uid_map,
const struct ugid_map* gid_map)
{
errcode_t retval;
struct inode_params params = {
.fs = fs,
.src_dir = src_dir,
.target_out = target_out,
.fs_config_func = fs_config_func,
.sehnd = sehnd,
.fixed_time = fixed_time,
.path = mountpoint,
.filename = mountpoint,
.mountpoint = mountpoint,
.uid_map = uid_map,
.gid_map = gid_map,
.error = 0
};
/* walk_dir will add the "/". Don't add it twice. */
if (strlen(mountpoint) == 1 && mountpoint[0] == '/')
params.path = "";
retval = androidify_inode(fs, EXT2_ROOT_INO, &params);
if (retval)
return retval;
retval = ext2fs_dir_iterate2(fs, EXT2_ROOT_INO, 0, NULL, walk_dir,
&params);
if (retval)
return retval;
return params.error;
}
errcode_t android_configure_fs(ext2_filsys fs, char *src_dir, char *target_out,
char *mountpoint,
struct selinux_opt *seopts EXT2FS_ATTR((unused)),
unsigned int nopt EXT2FS_ATTR((unused)),
char *fs_config_file, time_t fixed_time,
const struct ugid_map* uid_map,
const struct ugid_map* gid_map)
{
errcode_t retval;
fs_config_f fs_config_func = NULL;
struct selabel_handle *sehnd = NULL;
/* Retrieve file contexts */
#if !defined(__ANDROID__)
if (nopt > 0) {
sehnd = selabel_open(SELABEL_CTX_FILE, seopts, nopt);
if (!sehnd) {
int saved_errno = errno;
com_err(__func__, errno,
_("while opening file contexts \"%s\""),
seopts[0].value);
return saved_errno;
}
}
#else
sehnd = selinux_android_file_context_handle();
if (!sehnd) {
com_err(__func__, EINVAL,
_("while opening android file_contexts"));
return EINVAL;
}
#endif
/* Load the FS config */
if (fs_config_file) {
retval = load_canned_fs_config(fs_config_file);
if (retval < 0) {
com_err(__func__, retval,
_("while loading fs_config \"%s\""),
fs_config_file);
return retval;
}
fs_config_func = canned_fs_config;
} else if (mountpoint)
fs_config_func = fs_config;
return __android_configure_fs(fs, src_dir, target_out, mountpoint,
fs_config_func, sehnd, fixed_time,
uid_map, gid_map);
}

View file

@ -0,0 +1,66 @@
#ifndef ANDROID_PERMS_H
# define ANDROID_PERMS_H
# include "config.h"
# include <ext2fs/ext2fs.h>
typedef void (*fs_config_f)(const char *path, int dir,
const char *target_out_path,
unsigned *uid, unsigned *gid,
unsigned *mode, uint64_t *capabilities);
/*
* Represents a range of UID/GID mapping.
* This maps the id in [|parent_id|, |parent_id| + |length|) into
* [|child_id|, |child_id| + |length|)
*/
struct ugid_map_entry {
unsigned int child_id;
unsigned int parent_id;
unsigned int length;
};
struct ugid_map {
/* The number of elements in |entries|. */
size_t size;
/* An array of entries. If |size| is 0, this is a null pointer. */
struct ugid_map_entry* entries;
};
# ifdef _WIN32
struct selabel_handle;
static inline errcode_t android_configure_fs(ext2_filsys fs,
char *src_dir,
char *target_out,
char *mountpoint,
void *seopts,
unsigned int nopt,
char *fs_config_file,
time_t fixed_time,
const struct ugid_map* uid_map,
const struct ugdi_map* gid_map)
{
return 0;
}
# else
# include <selinux/selinux.h>
# include <selinux/label.h>
# if defined(__ANDROID__)
# include <selinux/android.h>
# endif
# include <private/android_filesystem_config.h>
# include <private/canned_fs_config.h>
# include <private/fs_config.h>
errcode_t android_configure_fs(ext2_filsys fs, char *src_dir,
char *target_out,
char *mountpoint,
struct selinux_opt *seopts,
unsigned int nopt,
char *fs_config_file, time_t fixed_time,
const struct ugid_map* uid_map,
const struct ugid_map* gid_map);
# endif
#endif /* !ANDROID_PERMS_H */

View file

@ -0,0 +1,45 @@
#!/bin/sh
# enable xtrace output if requested
if [ -n ${ENABLE_XTRACE:-''} ]; then
set -x
fi
# Build an e2fsprogs RPM from cvs
pwd=`pwd`
currdir=`basename $pwd`
pkgname=`grep Name: e2fsprogs.spec | awk '{print $2;}'`
pkgvers=`grep Version: e2fsprogs.spec | awk '{print $2;}'`
builddir=${pkgname}-${pkgvers}
# ensure that $TMP is set to something
TMP=${TMP:-'/tmp'}
cd ..
tmpdir=`mktemp -d ${RPM_TMPDIR:-$TMP}/rpmtmp.XXXXXX`
# We need to build a tarball for the SRPM using $builddir as the
# directory name (since that's what RPM will expect it to unpack
# into). That may require a symlink.
# Make a recursive-symlink copy of the source dir
cp -sR `pwd`/$currdir $tmpdir/$builddir || exit 1
# Remove any build files from the temporary tarball directory
[ -f $tmpdir/$builddir/Makefile ] && make -C $tmpdir/$builddir distclean
EXCLUDE="--exclude .hg* --exclude .pc*"
(cd $tmpdir && tar czfh ${builddir}.tar.gz $EXCLUDE $builddir)
[ "`rpmbuild --version 2> /dev/null`" ] && RPM=rpmbuild || RPM=rpm
$RPM --define "_sourcedir $tmpdir" \
--define "_topdir ${RPM_TOPDIR:-$(rpm -E %_topdir)}" \
--define "_tmpdir ${RPM_TMPDIR:-$TMP}" \
--define "extra_config_flags ${EXTRA_CONFIG_FLAGS:-''}" \
-ba $currdir/e2fsprogs.spec
rpm_exit=$?
rm -rf $tmpdir
exit $rpm_exit

View file

@ -0,0 +1,118 @@
#!/bin/sh
#$Id$
# Create Adobe-PostScript file that graphically displays the output of
# dumpe2fs(8). Use "dumpe2fs | dconf" to create a PostScript file on stdout.
# Developed and tested for Linux 1.0.
# Copyright (c) 1994
# Ulrich Windl
# ALte Regensburger Strasse 11a
# D-93149 Nittenau, Germany
# <Ulrich.Windl@rz.uni-regensburg.de>
SELF=`basename $0`
AWKFILE=/tmp/${SELF}.awk
TEMPFILE=/tmp/${SELF}.tmp
echo '
BEGIN {
print "B"
}
/^Inode count:/ {
ic=$3; next
}
/^Block count:/ {
bc=$3; next
}
/^First block:/ {
fb=$3; next
}
/^Block size:/ {
bs=$3; next
}
/^Blocks per group:/ {
bpg=$4
printf("BC %d\n", bpg)
printf("GC %d\n", (bc + bpg - 1) / bpg)
next
}
/^Inodes per group:/ {
ipg=$4; next
}
/^Last write time:/ {
lwtime=$0; gsub("Last write time:[ ]+", "", lwtime)
printf("T %s\n", lwtime)
next
}
/^Group [0-9]+:/ {
group=$2; gsub(":", "", group)
block=""
group_start=group*bpg+fb
group_end=group_start+bpg
printf("G %d : %d - %d\n", group, group_start, group_end)
next
}
/^[ ]+Free blocks: / {
for ( i=3; i < NF; ++i ) {
block=$i; gsub(",", "", block)
if ( index(block, "-") == 0 ) block=block "-" block
pos=index(block, "-")
printf("FB %d-%d\n",
substr(block, 0, pos) - group_start,
substr(block, pos + 1) - group_start)
}
if ( block == "" ) printf("Group %d is full\n", group)
print "----"
next
}
END {
printf("E %s\n", lwtime)
}' >$AWKFILE
awk -f $AWKFILE $* >$TEMPFILE
echo '
BEGIN {
printf("%%!PS-Adobe\n")
printf("%%%%BoundingBox: 0 0 1 1\n")
printf("/rect {/y2 exch def /x2 exch def /y1 exch def /x1 exch def\n")
printf(" newpath x1 y1 moveto x2 y1 lineto x2 y2 lineto\n")
printf(" x1 y2 lineto closepath} def\n")
printf("/fb {rect gsave 1.0 setgray fill grestore} def\n")
printf("/dg {rect gsave gsave 0.0 setgray fill grestore\n")
printf(" 0.5 setgray stroke grestore} def\n")
printf("/textxy {moveto show} bind def\n")
printf("0.0001 setlinewidth\n")
}
$1 == "GC" && NF == 2 {
number_of_groups=$2
printf("/Times-Roman findfont %g scalefont setfont\n",
1.0 / number_of_groups)
next
}
$1 == "BC" && NF == 2 {
blocks_per_group=$2; next
}
$1 == "T" && NF > 1 {
printf("(%s) %g %g textxy\n",
substr($0, 2), 0, 1.02)
next
}
$1 == "G" && NF == 6 && $3 == ":" && $5 == "-" {
group_index=$2
gs=$4
ge=$6
height=1.0 / number_of_groups
vstart=group_index * height
printf("%% group %d of %d:\n0 %g 1 %g dg\n",
group_index, number_of_groups, vstart, vstart + height)
printf("(Group %s) 1.02 %g textxy\n", group_index, vstart)
next
}
$1 == "FB" && NF == 2 {
pos = index($2, "-")
printf("%% hole %s\n%g %g %g %g fb\n",
$2, substr($2, 0, pos) / blocks_per_group, vstart,
(substr($2, pos + 1) + 1) / blocks_per_group, vstart + height)
next
}
END {
printf("%%%%EOF\n")
}
' >$AWKFILE
awk -f $AWKFILE $TEMPFILE

Some files were not shown because too many files have changed in this diff Show more