I was actually about to reply with something similar myself before I noticed there's three cases to distinguish. FWIW, here's my previous take - which adds a comma even when there's only two elements in the list:
sub serial {
my $last = '';
($last, $_[-1]) = ($_[-1], 'and ') if @_ > 1;
join(', ', @_) . $last;
}